package level import ( "encoding/json" "fmt" "os" "strings" ) type Level struct { ID string `json:"id"` Name string `json:"name"` Description string `json:"description"` Difficulty Difficulty `json:"difficulty"` TargetRPS int `json:"targetRps"` DurationSec int `json:"durationSec"` MaxMonthlyUSD int `json:"maxMonthlyUsd"` MaxP95LatencyMs int `json:"maxP95LatencyMs"` RequiredAvailabilityPct float64 `json:"requiredAvailabilityPct"` MustInclude []string `json:"mustInclude,omitempty"` MustNotInclude []string `json:"mustNotInclude,omitempty"` EncouragedComponents []string `json:"encouragedComponents,omitempty"` DiscouragedComponents []string `json:"discouragedComponents,omitempty"` MinReplicas map[string]int `json:"minReplicas,omitempty"` MaxLatencyPerNodeType map[string]int `json:"maxLatencyPerNodeType,omitempty"` CustomValidators []string `json:"customValidators,omitempty"` FailureEvents []FailureEvent `json:"failureEvents,omitempty"` ScoringWeights map[string]float64 `json:"scoringWeights,omitempty"` Hints []string `json:"hints,omitempty"` InterviewerRequirements []string `json:"interviewerRequirements,omitempty"` FunctionalRequirements []string `json:"functionalRequirements,omitempty"` NonFunctionalRequirements []string `json:"nonFunctionalRequirements,omitempty"` } type Difficulty string const ( DifficultyEasy Difficulty = "easy" DifficultyMedium Difficulty = "medium" DifficultyHard Difficulty = "hard" ) var Registry map[string]map[string]Level type FailureEvent struct { Type string `json:"type"` TimeSec int `json:"timeSec"` TargetID string `json:"targetId,omitempty"` } func LoadLevels(path string) ([]Level, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("Error opening levels.json: %w", err) } var levels []Level if err := json.Unmarshal(data, &levels); err != nil { return nil, fmt.Errorf("Error parsing levels.json: %w", err) } return levels, nil } func InitRegistry(levels []Level) { Registry = make(map[string]map[string]Level) for _, lvl := range levels { // check if level already exists here normalized := strings.ToLower(lvl.Name) if _, ok := Registry[normalized]; !ok { Registry[normalized] = make(map[string]Level) } // populate it Registry[normalized][string(lvl.Difficulty)] = lvl } } func GetLevel(name string, difficulty Difficulty) (*Level, error) { name = strings.ToLower(name) diffMap, ok := Registry[name] if !ok { return nil, fmt.Errorf("level name %s not found", name) } lvl, ok := diffMap[string(difficulty)] if !ok { return nil, fmt.Errorf("difficulty %s not available for level '%s'", difficulty, name) } return &lvl, nil } func (d *Difficulty) UnmarshalJSON(b []byte) error { var s string if err := json.Unmarshal(b, &s); err != nil { return err } switch s { case string(DifficultyEasy), string(DifficultyMedium), string(DifficultyHard): *d = Difficulty(s) return nil default: return fmt.Errorf("invalid difficulty: %q", s) } } func AllLevels() []Level { var levels []Level for _, diffMap := range Registry { for _, lvl := range diffMap { levels = append(levels, lvl) } } return levels }