plist_field(plist, key).ok_or_else(|| CommandError::Command(format!("{ctx}: missing {key}")))
usize::from_str(&s).map_err(|e| CommandError::Command(format!("{ctx}: {key} parse error: {e}")))
pub fn value_to_activity_periods(value: &Value) -> Result<Vec<PeriodActivityView>, CommandError> {
pub fn value_to_breakdown_periods(value: &Value) -> Result<Vec<BreakdownPeriodView>, CommandError> {
let wire = r#"(:balance-report :periods ((:label nil :roots ((:account-id "00000000-0000-0000-0000-000000000000" :account-name "Assets" :account-path "Assets" :depth 0 :account-type "asset" :amounts ((:commodity-id "00000000-0000-0000-0000-000000000000" :symbol "USD" :amount 100) (:commodity-id "00000000-0000-0000-0000-000000000000" :symbol "EUR" :amount 50)) :children ())))))"#;
let wire = r#"(:activity-report :meta nil :periods ((:label "2026-Q1" :groups ((:label "Income" :flip-sign t :roots ((:account-id "00000000-0000-0000-0000-000000000000" :account-name "Salary" :account-path "Income:Salary" :depth 0 :account-type nil :amounts ((:commodity-id "00000000-0000-0000-0000-000000000000" :symbol "USD" :amount 1000)) :children ())))))))"#;
let wire = r#"(:category-breakdown :meta nil :tag-name "category" :periods ((:label nil :rows ((:tag-value "Food" :uncategorized nil :amounts ((:commodity-id "00000000-0000-0000-0000-000000000000" :symbol "USD" :amount 200)))))))"#;