|
1 | 1 | // SPDX-License-Identifier: PMPL-1.0-or-later |
2 | 2 | // Copyright (c) 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk> |
3 | | -// ABI module for mylangiser — Idris2 proof types for My-Lang interface correctness. |
| 3 | +// |
| 4 | +// ABI module for mylangiser — core types representing the progressive-disclosure |
| 5 | +// interface model. These types mirror the Idris2 formal proofs in src/interface/abi/ |
| 6 | +// and provide the Rust-side representations used throughout the codegen pipeline. |
| 7 | +// |
| 8 | +// The Idris2 ABI (Types.idr, Layout.idr, Foreign.idr) provides formal verification |
| 9 | +// of these type relationships; this module is the Rust counterpart used at runtime. |
| 10 | + |
| 11 | +use serde::{Deserialize, Serialize}; |
| 12 | + |
| 13 | +/// Disclosure level for a function/endpoint, determined by its complexity score. |
| 14 | +/// |
| 15 | +/// - `Beginner`: Simple functions with few required params (score 0-30). |
| 16 | +/// Only required parameters are exposed; all optionals get smart defaults. |
| 17 | +/// - `Intermediate`: Moderate complexity (score 30-70). Required params plus |
| 18 | +/// commonly-used optional params (scalar types, those with known defaults). |
| 19 | +/// - `Expert`: Full API surface (score 70-100). Every parameter exposed, |
| 20 | +/// no smart defaults applied. |
| 21 | +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] |
| 22 | +pub enum DisclosureLevel { |
| 23 | + /// Simplified view: required params only, smart defaults for the rest. |
| 24 | + Beginner, |
| 25 | + /// Balanced view: required + commonly-used optional params. |
| 26 | + Intermediate, |
| 27 | + /// Full view: all parameters exposed, no defaults applied. |
| 28 | + Expert, |
| 29 | +} |
| 30 | + |
| 31 | +/// A complexity score in the range 0-100, representing how complex an |
| 32 | +/// API endpoint is. The score is calculated from weighted factors: |
| 33 | +/// parameter count, optional ratio, type complexity, and required count. |
| 34 | +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] |
| 35 | +pub struct ComplexityScore { |
| 36 | + /// The numeric complexity value (0 = trivial, 100 = maximally complex). |
| 37 | + pub value: u32, |
| 38 | +} |
| 39 | + |
| 40 | +impl ComplexityScore { |
| 41 | + /// Create a new complexity score, clamping to the valid 0-100 range. |
| 42 | + pub fn new(value: u32) -> Self { |
| 43 | + Self { |
| 44 | + value: value.min(100), |
| 45 | + } |
| 46 | + } |
| 47 | +} |
| 48 | + |
| 49 | +impl std::fmt::Display for ComplexityScore { |
| 50 | + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| 51 | + write!(f, "{}/100", self.value) |
| 52 | + } |
| 53 | +} |
| 54 | + |
| 55 | +/// An API endpoint with its parsed metadata, complexity score, and |
| 56 | +/// assigned disclosure level. |
| 57 | +#[derive(Debug, Clone, Serialize, Deserialize)] |
| 58 | +pub struct APIEndpoint { |
| 59 | + /// The endpoint/function name. |
| 60 | + pub name: String, |
| 61 | + /// Total number of parameters. |
| 62 | + pub param_count: usize, |
| 63 | + /// Number of required parameters. |
| 64 | + pub required_count: usize, |
| 65 | + /// Number of optional parameters. |
| 66 | + pub optional_count: usize, |
| 67 | + /// Calculated complexity score. |
| 68 | + pub complexity: ComplexityScore, |
| 69 | + /// Assigned disclosure level based on complexity thresholds. |
| 70 | + pub level: DisclosureLevel, |
| 71 | +} |
| 72 | + |
| 73 | +/// A smart default value applied to an optional parameter when it is |
| 74 | +/// omitted at lower disclosure levels. The parameter name maps to the |
| 75 | +/// default value (as a string representation). |
| 76 | +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] |
| 77 | +pub struct SmartDefault { |
| 78 | + /// The parameter name this default applies to. |
| 79 | + pub param_name: String, |
| 80 | + /// The default value as a display string (e.g., `"user"`, `20`, `true`). |
| 81 | + pub default_value: String, |
| 82 | +} |
| 83 | + |
| 84 | +/// A layered wrapper for a single endpoint, containing the function |
| 85 | +/// signatures and generated code for each disclosure level. |
| 86 | +#[derive(Debug, Clone, Serialize, Deserialize)] |
| 87 | +pub struct LayeredWrapper { |
| 88 | + /// The original endpoint name. |
| 89 | + pub endpoint_name: String, |
| 90 | + /// Beginner-level parameter list (required params only). |
| 91 | + pub beginner_signature: Vec<String>, |
| 92 | + /// Intermediate-level parameter list (required + common optional). |
| 93 | + pub intermediate_signature: Vec<String>, |
| 94 | + /// Expert-level parameter list (all params). |
| 95 | + pub expert_signature: Vec<String>, |
| 96 | + /// Smart defaults applied at beginner/intermediate levels. |
| 97 | + pub smart_defaults: Vec<SmartDefault>, |
| 98 | + /// Generated wrapper code for beginner level. |
| 99 | + pub beginner_code: String, |
| 100 | + /// Generated wrapper code for intermediate level. |
| 101 | + pub intermediate_code: String, |
| 102 | + /// Generated wrapper code for expert level. |
| 103 | + pub expert_code: String, |
| 104 | +} |
| 105 | + |
| 106 | +#[cfg(test)] |
| 107 | +mod tests { |
| 108 | + use super::*; |
| 109 | + |
| 110 | + #[test] |
| 111 | + fn test_complexity_score_clamped() { |
| 112 | + let score = ComplexityScore::new(150); |
| 113 | + assert_eq!(score.value, 100); |
| 114 | + } |
| 115 | + |
| 116 | + #[test] |
| 117 | + fn test_complexity_score_display() { |
| 118 | + let score = ComplexityScore::new(42); |
| 119 | + assert_eq!(format!("{}", score), "42/100"); |
| 120 | + } |
| 121 | + |
| 122 | + #[test] |
| 123 | + fn test_disclosure_level_equality() { |
| 124 | + assert_eq!(DisclosureLevel::Beginner, DisclosureLevel::Beginner); |
| 125 | + assert_ne!(DisclosureLevel::Beginner, DisclosureLevel::Expert); |
| 126 | + } |
| 127 | +} |
0 commit comments