internal/shadowdiff/config.go
Source
- Package:
shadowdiff - File:
internal/shadowdiff/config.go - GitHub: https://github.com/theroutercompany/api_router/blob/main/internal/shadowdiff/config.go
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
import (
"encoding/json"
"fmt"
"os"
)
Types
type block 1
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.
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
returnstatement (possibly returning values).
- L21:
- 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
returnstatement (possibly returning values).
- L25:
- 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
returnstatement (possibly returning values).
- L30:
- 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.
- L34:
- 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
returnstatement (possibly returning values).