Skip to main content

internal/shadowdiff/config.go

Source

Overview

What: Defines the JSON configuration schema for a shadow diff session and provides a loader with basic validation.

Why:

Shadow diff needs a small amount of runtime configuration:

  • two base URLs to compare (Node and Go)
  • a list of fixture files to replay
  • a concurrency level for running fixtures in parallel

Keeping the schema small and JSON-based makes it easy for scripts to rewrite parts of the config (for example, injecting a temporary base URL for a local gateway instance).

How:

LoadConfig(path) reads the JSON file into Config, validates required fields, applies a default concurrency value when unset, and returns the resulting struct.

Notes:

The loader currently validates only the presence of base URLs and a positive concurrency value. Fixture paths are validated later when fixtures are loaded.

Contents

Imports

import block 1

internal/shadowdiff/config.go#L3
import (
"encoding/json"
"fmt"
"os"
)

Types

type block 1

internal/shadowdiff/config.go#L10
type Config struct {
NodeBaseURL string `json:"nodeBaseUrl"`
GoBaseURL string `json:"goBaseUrl"`
Fixtures []string `json:"fixtures"`
Concurrency int `json:"concurrency"`
}

Config

What: Configuration values required to run a shadow diff session.

Why: Centralizes run parameters so the runner and CLI do not need additional global state.

How: JSON-tagged fields provide base URLs, fixture file paths, and a concurrency limit for the runner.

Notes: Base URLs should include scheme and host (and port if needed).

Functions and Methods

LoadConfig

What: Reads configuration from a JSON file on disk.

Why: Ensures a shadow diff run has the minimum required inputs and sane defaults.

How: Reads the file, unmarshals into Config, checks required base URLs, applies a default concurrency when missing, and returns the final config.

internal/shadowdiff/config.go#L18
func LoadConfig(path string) (Config, error) {
data, err := os.ReadFile(path)
if err != nil {
return Config{}, fmt.Errorf("read config: %w", err)
}

var cfg Config
if err := json.Unmarshal(data, &cfg); err != nil {
return Config{}, fmt.Errorf("decode config: %w", err)
}

if cfg.NodeBaseURL == "" || cfg.GoBaseURL == "" {
return Config{}, fmt.Errorf("nodeBaseUrl and goBaseUrl are required")
}

if cfg.Concurrency <= 0 {
cfg.Concurrency = 4
}

return cfg, nil
}

Walkthrough

The list below documents the statements inside the function body, including nested blocks and inline closures.

  • L19: data, err := os.ReadFile(path)
    • What: Defines data, err.
    • Why: Keeps intermediate state available for later steps in the function.
    • How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
  • L20: if err != nil { return Config{}, fmt.Errorf("read config: %w", err) }
    • What: Branches conditionally.
    • Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
    • How: Evaluates the condition and executes the matching branch.
    • Nested steps:
      • L21: return Config{}, fmt.Errorf("read config: %w", err)
        • What: Returns from the current function.
        • Why: Ends the current execution path and hands control back to the caller.
        • How: Executes a return statement (possibly returning values).
  • L24: var cfg Config
    • What: Declares local names.
    • Why: Introduces variables or types used later in the function.
    • How: Executes a Go declaration statement inside the function body.
  • L25: if err := json.Unmarshal(data, &cfg); err != nil { return Config{}, fmt.Errorf("decode config: %w", err) }
    • What: Branches conditionally.
    • Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
    • How: Evaluates the condition and executes the matching branch.
    • Nested steps:
      • L25: err := json.Unmarshal(data, &cfg)
        • What: Defines err.
        • Why: Keeps intermediate state available for later steps in the function.
        • How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
      • L26: return Config{}, fmt.Errorf("decode config: %w", err)
        • What: Returns from the current function.
        • Why: Ends the current execution path and hands control back to the caller.
        • How: Executes a return statement (possibly returning values).
  • L29: if cfg.NodeBaseURL == "" || cfg.GoBaseURL == "" { return Config{}, fmt.Errorf("nodeBaseUrl and goBaseUrl are required") }
    • What: Branches conditionally.
    • Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
    • How: Evaluates the condition and executes the matching branch.
    • Nested steps:
      • L30: return Config{}, fmt.Errorf("nodeBaseUrl and goBaseUrl are required")
        • What: Returns from the current function.
        • Why: Ends the current execution path and hands control back to the caller.
        • How: Executes a return statement (possibly returning values).
  • L33: if cfg.Concurrency <= 0 { cfg.Concurrency = 4 }
    • What: Branches conditionally.
    • Why: Handles different execution paths based on runtime state.
    • How: Evaluates the condition and executes the matching branch.
    • Nested steps:
      • L34: cfg.Concurrency = 4
        • What: Assigns cfg.Concurrency.
        • Why: Keeps intermediate state available for later steps in the function.
        • How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
  • L37: return cfg, nil
    • What: Returns from the current function.
    • Why: Ends the current execution path and hands control back to the caller.
    • How: Executes a return statement (possibly returning values).

Guides

Neighboring source