Skip to main content

problemreductions/registry/
schema.rs

1//! Problem schema registration via inventory.
2
3use super::FieldInfo;
4use serde::Serialize;
5
6/// A declared variant dimension for a problem type.
7///
8/// Describes one axis of variation (e.g., graph type, weight type) with
9/// its default value and the set of allowed values.
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct VariantDimension {
12    /// Dimension key (e.g., `"graph"`, `"weight"`, `"k"`).
13    pub key: &'static str,
14    /// Default value for this dimension (e.g., `"SimpleGraph"`).
15    pub default_value: &'static str,
16    /// All allowed values for this dimension.
17    pub allowed_values: &'static [&'static str],
18}
19
20impl VariantDimension {
21    /// Create a new variant dimension.
22    pub const fn new(
23        key: &'static str,
24        default_value: &'static str,
25        allowed_values: &'static [&'static str],
26    ) -> Self {
27        Self {
28            key,
29            default_value,
30            allowed_values,
31        }
32    }
33}
34
35/// A registered problem schema entry for static inventory registration.
36pub struct ProblemSchemaEntry {
37    /// Problem name (e.g., "MaximumIndependentSet").
38    pub name: &'static str,
39    /// Human-readable display name (e.g., "Maximum Independent Set").
40    pub display_name: &'static str,
41    /// Short aliases for CLI/MCP lookup (e.g., `&["MIS"]`).
42    pub aliases: &'static [&'static str],
43    /// Declared variant dimensions with defaults and allowed values.
44    pub dimensions: &'static [VariantDimension],
45    /// Module path from `module_path!()` (e.g., "problemreductions::models::graph::maximum_independent_set").
46    pub module_path: &'static str,
47    /// Human-readable description.
48    pub description: &'static str,
49    /// Struct fields.
50    pub fields: &'static [FieldInfo],
51}
52
53inventory::collect!(ProblemSchemaEntry);
54
55/// Optional static size-field metadata for problem types.
56///
57/// This is used when a problem has meaningful size fields even before it
58/// participates in any reduction overhead expressions.
59pub struct ProblemSizeFieldEntry {
60    /// Problem name (e.g., "MaximumIndependentSet").
61    pub name: &'static str,
62    /// Size field names (e.g., `&["num_vertices", "num_edges"]`).
63    pub fields: &'static [&'static str],
64}
65
66inventory::collect!(ProblemSizeFieldEntry);
67
68/// JSON-serializable problem schema.
69#[derive(Debug, Clone, Serialize)]
70pub struct ProblemSchemaJson {
71    /// Problem name.
72    pub name: String,
73    /// Problem description.
74    pub description: String,
75    /// Struct fields.
76    pub fields: Vec<FieldInfoJson>,
77}
78
79/// JSON-serializable field info.
80#[derive(Debug, Clone, Serialize)]
81pub struct FieldInfoJson {
82    /// Field name.
83    pub name: String,
84    /// Field type.
85    pub type_name: String,
86    /// Field description.
87    pub description: String,
88}
89
90/// Collect all registered problem schemas into JSON-serializable form.
91pub fn collect_schemas() -> Vec<ProblemSchemaJson> {
92    let mut schemas: Vec<ProblemSchemaJson> = inventory::iter::<ProblemSchemaEntry>
93        .into_iter()
94        .map(|entry| ProblemSchemaJson {
95            name: entry.name.to_string(),
96            description: entry.description.to_string(),
97            fields: entry
98                .fields
99                .iter()
100                .map(|f| FieldInfoJson {
101                    name: f.name.to_string(),
102                    type_name: f.type_name.to_string(),
103                    description: f.description.to_string(),
104                })
105                .collect(),
106        })
107        .collect();
108    schemas.sort_by(|a, b| a.name.cmp(&b.name));
109    schemas
110}
111
112/// Collect explicitly declared size fields for a problem type.
113pub fn declared_size_fields(name: &str) -> Vec<&'static str> {
114    inventory::iter::<ProblemSizeFieldEntry>()
115        .filter(|entry| entry.name == name)
116        .flat_map(|entry| entry.fields.iter().copied())
117        .collect()
118}
119
120#[cfg(test)]
121#[path = "../unit_tests/registry/schema.rs"]
122mod tests;