magi-code 0.78.0

Repository-aware CLI coding agent for terminal work
Documentation
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct UsageReport {
    pub(crate) generated_at: Option<String>,
    pub(crate) providers: Vec<UsageProviderRow>,
    pub(crate) help: Vec<String>,
    pub(crate) warning: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct UsageProviderRow {
    pub(crate) provider: String,
    pub(crate) label: String,
    pub(crate) source: String,
    pub(crate) plan: Option<String>,
    pub(crate) status: String,
    pub(crate) windows: Vec<UsageWindowRow>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct UsageWindowRow {
    pub(crate) label: String,
    pub(crate) kind: String,
    pub(crate) percent_remaining_basis_points: u16,
    pub(crate) percent_used_basis_points: Option<u16>,
    pub(crate) reset_text: Option<String>,
    pub(crate) resets_at: Option<String>,
    pub(crate) spent_usd: Option<String>,
    pub(crate) limit_usd: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum UsageLoadResult {
    Loaded(UsageReport),
    Error(String),
}

pub(crate) fn usage_report(usage: crate::providers::codex_usage::CodexUsage) -> UsageReport {
    let windows = usage
        .windows
        .into_iter()
        .map(|window| {
            let resets_at = window
                .reset_at
                .and_then(|timestamp| chrono::DateTime::from_timestamp(timestamp, 0))
                .map(|time| time.to_rfc3339());
            UsageWindowRow {
                label: window.label,
                kind: "rolling".to_string(),
                percent_remaining_basis_points: percent_to_basis_points(
                    100.0 - window.used_percent,
                ),
                percent_used_basis_points: Some(percent_to_basis_points(window.used_percent)),
                reset_text: None,
                resets_at,
                spent_usd: None,
                limit_usd: None,
            }
        })
        .collect();
    UsageReport {
        generated_at: Some(chrono::Utc::now().to_rfc3339()),
        providers: vec![UsageProviderRow {
            provider: "openai-codex".to_string(),
            label: "OpenAI Codex".to_string(),
            source: "API".to_string(),
            plan: usage.plan,
            status: "fresh".to_string(),
            windows,
        }],
        help: Vec::new(),
        warning: None,
    }
}

fn percent_to_basis_points(value: f64) -> u16 {
    (value.clamp(0.0, 100.0) * 100.0).round() as u16
}

pub(crate) fn format_percent_basis_points(basis_points: u16) -> String {
    let whole = basis_points / 100;
    let tenth = (basis_points % 100) / 10;
    if tenth == 0 {
        format!("{whole}%")
    } else {
        format!("{whole}.{tenth}%")
    }
}

/// Plain-text content for the usage modal states that render as a single
/// wrapped block of text. Returns `None` for the loaded-with-providers state,
/// which renders as a mix of text and gauge rows instead. Shared by the
/// renderer and the scroll-overflow calculation so the two never disagree
/// about how many visual rows the content occupies.
pub(crate) fn usage_text_content(
    loading: bool,
    result: Option<&UsageLoadResult>,
) -> Option<String> {
    if loading || result.is_none() {
        return Some("Loading provider usage…".to_string());
    }
    match result.unwrap() {
        UsageLoadResult::Error(message) => Some(format!(
            "Could not load usage: {message}\n\nRerun /usage to retry."
        )),
        UsageLoadResult::Loaded(report) if report.providers.is_empty() => {
            let mut text = "No Codex usage windows available.".to_string();
            if !report.help.is_empty() {
                text.push_str("\n\n");
                text.push_str(&report.help.join("\n"));
            }
            Some(text)
        }
        UsageLoadResult::Loaded(_) => None,
    }
}

pub(crate) fn usage_visual_rows(
    loading: bool,
    result: Option<&UsageLoadResult>,
    wrap_width: u16,
) -> usize {
    if let Some(text) = usage_text_content(loading, result) {
        return wrapped_rows(&text, wrap_width);
    }
    let Some(UsageLoadResult::Loaded(report)) = result else {
        return 0;
    };
    usize::from(report.generated_at.is_some())
        + report
            .providers
            .iter()
            .map(|provider| 1 + provider.windows.len() * 2 + 1)
            .sum::<usize>()
}

fn wrapped_rows(text: &str, wrap_width: u16) -> usize {
    crate::tui::transcript::ratatui_wrapped_visual_rows(text, wrap_width.max(1))
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::providers::codex_usage::CodexUsage;
    use crate::providers::codex_usage::CodexUsageWindow;

    #[test]
    fn shows_live_codex_remaining_usage_and_reset() {
        let report = usage_report(CodexUsage {
            plan: Some("plus".to_string()),
            windows: vec![CodexUsageWindow {
                label: "5h".to_string(),
                limit_window_seconds: Some(18_000),
                used_percent: 42.5,
                reset_at: Some(1_700_000_000),
            }],
        });
        let provider = &report.providers[0];
        assert_eq!(provider.provider, "openai-codex");
        assert_eq!(provider.status, "fresh");
        assert_eq!(provider.windows[0].percent_remaining_basis_points, 5750);
        assert_eq!(provider.windows[0].percent_used_basis_points, Some(4250));
        assert_eq!(
            provider.windows[0].resets_at.as_deref(),
            Some("2023-11-14T22:13:20+00:00")
        );
    }

    #[test]
    fn usage_errors_show_retry_without_external_cli_instructions() {
        let result = UsageLoadResult::Error("Codex usage request timed out".to_string());
        let text = usage_text_content(false, Some(&result)).unwrap();
        assert!(text.contains("timed out"));
        assert!(text.contains("Rerun /usage"));
        assert!(usage_visual_rows(false, Some(&result), 20) > 3);
    }
}