package main import ( "encoding/json" "fmt" "os" ) func readJSON(path string, v any) error { body, err := os.ReadFile(path) if err != nil { return err } return json.Unmarshal(body, v) } // writeJSON replaces the file atomically: a half-written state file would look // like a corrupt install on the next run. func writeJSON(path string, v any) error { body, err := json.MarshalIndent(v, "", " ") if err != nil { return err } tmp := fmt.Sprintf("%s.%d", path, os.Getpid()) addTemp(tmp) defer dropTemp(tmp) if err := os.WriteFile(tmp, append(body, '\n'), 0o644); err != nil { return fmt.Errorf("cannot write %s: %w", tmp, err) } if err := os.Rename(tmp, path); err != nil { os.Remove(tmp) return fmt.Errorf("cannot update %s: %w", path, err) } return nil } func humanSize(n int64) string { switch { case n >= 1024*1024: return fmt.Sprintf("%.1f MB", float64(n)/1024/1024) case n >= 1024: return fmt.Sprintf("%.1f kB", float64(n)/1024) } return fmt.Sprintf("%d B", n) }