package main // getString extracts a string value from a content map. func getString(content map[string]any, key string) string { if v, ok := content[key].(string); ok { return v } return "" } // getStringDefault returns the string at key, falling back to def if missing/blank. func getStringDefault(content map[string]any, key, def string) string { if v := getString(content, key); v != "" { return v } return def } // getBool extracts a boolean from a content map. Accepts native bool, string ("true"/"1"), // or numeric truthiness. Returns def when no value is present or parseable. func getBool(content map[string]any, key string, def bool) bool { if v, ok := content[key]; ok { switch t := v.(type) { case bool: return t case string: switch t { case "true", "TRUE", "True", "1", "yes", "on": return true case "false", "FALSE", "False", "0", "no", "off": return false } case float64: return t != 0 case int: return t != 0 } } return def } // getSlice extracts a slice of maps from content under key. Returns nil if not a slice. func getSlice(content map[string]any, key string) []map[string]any { if v, ok := content[key].([]any); ok { out := make([]map[string]any, 0, len(v)) for _, item := range v { if m, ok := item.(map[string]any); ok { out = append(out, m) } } return out } return nil } // getMap returns the map at key, or nil. func getMap(content map[string]any, key string) map[string]any { if m, ok := content[key].(map[string]any); ok { return m } return nil } // getFloat returns the float at key, or def if missing / wrong type. func getFloat(content map[string]any, key string, def float64) float64 { if v, ok := content[key].(float64); ok { return v } if v, ok := content[key].(int); ok { return float64(v) } return def } // normalizeTrend coerces an incoming trend string to {up, down, flat}, defaulting to flat. func normalizeTrend(s string) string { switch s { case "up", "down", "flat": return s } return "flat" }