progit-plugin-sdk 0.2.1

Plugin SDK for ProGit — sandboxed LuaJIT runtime with capability-based security. LSL-1.0 (file-level copyleft, proprietary plugins allowed via the commercial bridge).
Documentation
// SPDX-License-Identifier: LSL-1.0
// Copyright (c) 2025 Markus Maiwald

//! Analytics plugin traits
//!
//! Traits for plugins that compute metrics, generate reports, or export data.

use serde::{Deserialize, Serialize};
use std::collections::HashMap;

use super::core::{Issue, Plugin, PluginResult};

/// Query parameters for filtering issues in analytics
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct AnalyticsQuery {
    /// Date range for the query
    pub date_range: Option<DateRange>,
    /// Filter by statuses
    pub statuses: Vec<String>,
    /// Filter by tags
    pub tags: Vec<String>,
    /// Filter by assignees
    pub assignees: Vec<String>,
    /// Filter by sprints
    pub sprints: Vec<u32>,
    /// Include deleted issues
    pub include_deleted: bool,
    /// Maximum number of results
    pub limit: Option<usize>,
    /// Offset for pagination
    pub offset: Option<usize>,
}

/// Date range for queries
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DateRange {
    /// Start date (ISO 8601)
    pub start: String,
    /// End date (ISO 8601)
    pub end: String,
}

/// Types of metrics that can be computed
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum MetricType {
    /// Issues completed per sprint/week
    Velocity,
    /// Time from created to done
    LeadTime,
    /// Time from in-progress to done
    CycleTime,
    /// Total issues completed in period
    Throughput,
    /// Work in progress count
    WIP,
    /// Percentage of issues that are blocked
    BlockedRatio,
    /// Estimated vs actual effort accuracy
    EffortAccuracy,
    /// Number of issues by status
    StatusDistribution,
    /// Number of issues by tag
    TagDistribution,
    /// Number of issues by assignee
    AssigneeWorkload,
    /// Custom metric with name
    Custom(String),
}

/// Result of a metric computation
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MetricValue {
    /// The metric that was computed
    pub metric: MetricType,
    /// The computed value
    pub value: f64,
    /// Unit of measurement (e.g., "issues", "days", "percent")
    pub unit: String,
    /// Breakdown by category (per-tag, per-assignee, etc.)
    pub breakdown: Option<HashMap<String, f64>>,
    /// Trend compared to previous period
    pub trend: Option<Trend>,
}

/// Trend indicator for metrics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Trend {
    /// Change from previous period
    pub change: f64,
    /// Percentage change
    pub change_percent: f64,
    /// Direction of change
    pub direction: TrendDirection,
}

/// Direction of trend change
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum TrendDirection {
    Up,
    Down,
    Stable,
}

/// Types of reports that can be generated
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum ReportType {
    /// Burndown chart data
    Burndown,
    /// Burn-up chart data
    BurnUp,
    /// Velocity trend over time
    VelocityTrend,
    /// Cumulative flow diagram data
    CumulativeFlow,
    /// Lead time trend over time
    LeadTimeTrend,
    /// Tag distribution pie chart
    TagDistribution,
    /// Assignee workload bar chart
    AssigneeWorkload,
    /// Sprint summary
    SprintSummary,
    /// Custom report with name
    Custom(String),
}

/// A generated report
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Report {
    /// Type of report
    pub report_type: ReportType,
    /// Report title
    pub title: String,
    /// When the report was generated (ISO 8601)
    pub generated_at: String,
    /// Data points for charting
    pub data_points: Vec<DataPoint>,
    /// Summary statistics
    pub summary: HashMap<String, serde_json::Value>,
    /// Report-specific metadata
    pub metadata: HashMap<String, serde_json::Value>,
}

/// A single data point in a report
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DataPoint {
    /// X-axis label (date, sprint number, etc.)
    pub label: String,
    /// Y-axis values (can have multiple series)
    pub values: HashMap<String, f64>,
}

/// Export formats for data
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum ExportFormat {
    /// JSON format
    Json,
    /// CSV format
    Csv,
    /// Markdown format
    Markdown,
    /// HTML format
    Html,
    /// Custom format with name
    Custom(String),
}

/// Historical issue snapshot for analytics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IssueSnapshot {
    /// The issue at this point in time
    pub issue: Issue,
    /// When this snapshot was taken (ISO 8601)
    pub snapshot_time: String,
    /// Status at snapshot time
    pub status_at_time: String,
    /// Sprint at snapshot time
    pub sprint_at_time: Option<u32>,
}

/// Sprint definition
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Sprint {
    /// Sprint number
    pub number: u32,
    /// Sprint name (optional)
    pub name: Option<String>,
    /// Start date (ISO 8601)
    pub start_date: String,
    /// End date (ISO 8601)
    pub end_date: String,
    /// Sprint goal
    pub goal: Option<String>,
    /// Total effort committed
    pub committed_effort: u32,
    /// Total effort completed
    pub completed_effort: u32,
}

/// Status transition record
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StatusTransition {
    /// Issue ID
    pub issue_id: String,
    /// Previous status
    pub from_status: String,
    /// New status
    pub to_status: String,
    /// When the transition occurred (ISO 8601)
    pub timestamp: String,
    /// Who made the change
    pub by_user: Option<String>,
}

/// Trait for plugins that compute metrics, generate reports, or export data
pub trait AnalyticsPlugin: Plugin {
    /// Query issues with filters
    ///
    /// Returns issues matching the query parameters.
    fn query_issues(&mut self, query: &AnalyticsQuery) -> PluginResult<Vec<Issue>>;

    /// Compute a specific metric
    ///
    /// Calculates the requested metric for the given date range.
    fn compute_metric(
        &mut self,
        metric: MetricType,
        range: &DateRange,
    ) -> PluginResult<MetricValue>;

    /// Generate a report
    ///
    /// Creates a full report of the requested type for the given date range.
    fn generate_report(
        &mut self,
        report_type: ReportType,
        range: &DateRange,
    ) -> PluginResult<Report>;

    /// Export data in specified format
    ///
    /// Exports filtered data in the requested format. Returns raw bytes.
    fn export(
        &mut self,
        format: ExportFormat,
        query: &AnalyticsQuery,
    ) -> PluginResult<Vec<u8>>;

    /// Get available metrics
    ///
    /// Returns list of metrics this plugin can compute.
    fn available_metrics(&self) -> Vec<MetricType> {
        vec![
            MetricType::Velocity,
            MetricType::LeadTime,
            MetricType::CycleTime,
            MetricType::Throughput,
            MetricType::WIP,
        ]
    }

    /// Get available report types
    ///
    /// Returns list of reports this plugin can generate.
    fn available_reports(&self) -> Vec<ReportType> {
        vec![
            ReportType::Burndown,
            ReportType::VelocityTrend,
        ]
    }

    /// Get available export formats
    ///
    /// Returns list of export formats this plugin supports.
    fn available_formats(&self) -> Vec<ExportFormat> {
        vec![
            ExportFormat::Json,
            ExportFormat::Csv,
        ]
    }

    /// Get historical snapshots
    ///
    /// Returns historical issue snapshots for trend analysis.
    fn get_snapshots(
        &mut self,
        _range: &DateRange,
    ) -> PluginResult<Vec<IssueSnapshot>> {
        // Default: no historical data
        Ok(vec![])
    }

    /// Get status transitions
    ///
    /// Returns status transition history for cycle time analysis.
    fn get_transitions(
        &mut self,
        _range: &DateRange,
    ) -> PluginResult<Vec<StatusTransition>> {
        // Default: no transition data
        Ok(vec![])
    }

    /// Get sprint definitions
    ///
    /// Returns sprint information for sprint-based reports.
    fn get_sprints(&mut self) -> PluginResult<Vec<Sprint>> {
        // Default: no sprint data
        Ok(vec![])
    }
}