Skip to content

Commit 13e7673

Browse files
hyperpolymathclaude
andcommitted
feat: implement Phase 1 progressive-disclosure codegen pipeline
Implement the core mylangiser functionality: manifest parsing for API endpoints, weighted complexity scoring (param count, optional ratio, type complexity, required count), disclosure level assignment (beginner/intermediate/expert), and layered wrapper code generation with smart defaults for omitted parameters. - manifest/mod.rs: Rewritten for endpoint-based API manifest schema - codegen/parser.rs: Parse param definitions with types and optionality - codegen/scorer.rs: Weighted 0-100 complexity scoring algorithm - codegen/layer_gen.rs: Generate beginner/intermediate/expert wrappers - abi/mod.rs: DisclosureLevel, ComplexityScore, APIEndpoint, SmartDefault, LayeredWrapper - tests/integration_test.rs: 8 integration tests covering full pipeline - examples/simplified-api/mylangiser.toml: Example manifest 48 tests passing (20 unit lib + 20 unit bin + 8 integration). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 90f5073 commit 13e7673

11 files changed

Lines changed: 2548 additions & 47 deletions

File tree

Cargo.lock

Lines changed: 906 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# SPDX-License-Identifier: PMPL-1.0-or-later
2+
# mylangiser manifest — progressive-disclosure interface for a user management API.
3+
#
4+
# This example demonstrates how mylangiser analyses endpoint complexity and
5+
# generates layered wrappers:
6+
# - Beginner: create_user(username, email) — 2 required params, smart defaults
7+
# - Intermediate: create_user(username, email, role?, mfa?, locale?) — adds common optionals
8+
# - Expert: create_user(username, email, role?, permissions?, mfa?, locale?) — full API
9+
10+
[project]
11+
name = "simplified-api"
12+
description = "Progressive-disclosure wrapper for a user management and search API"
13+
14+
[[endpoints]]
15+
name = "create_user"
16+
description = "Create a new user account with progressive parameter disclosure"
17+
params = [
18+
"username: string",
19+
"email: string",
20+
"role?: string",
21+
"permissions?: list",
22+
"mfa?: bool",
23+
"locale?: string",
24+
]
25+
required = ["username", "email"]
26+
27+
[[endpoints]]
28+
name = "search"
29+
description = "Search with progressive filter disclosure"
30+
params = [
31+
"query: string",
32+
"filters?: map",
33+
"sort?: string",
34+
"page?: int",
35+
"limit?: int",
36+
"cursor?: string",
37+
"facets?: list",
38+
]
39+
required = ["query"]
40+
41+
[[endpoints]]
42+
name = "get_user"
43+
description = "Simple user lookup — always beginner-level"
44+
params = [
45+
"user_id: string",
46+
]
47+
required = ["user_id"]
48+
49+
[levels]
50+
beginner-threshold = 30
51+
expert-threshold = 70
52+
53+
[defaults]
54+
role = "user"
55+
limit = 20
56+
page = 1
57+
locale = "en-GB"

src/abi/mod.rs

Lines changed: 125 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,127 @@
11
// SPDX-License-Identifier: PMPL-1.0-or-later
22
// 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

Comments
 (0)