problemreductions/registry/
schema.rs1use super::FieldInfo;
4use serde::Serialize;
5use std::fmt;
6use std::str::FromStr;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
10#[serde(rename_all = "lowercase")]
11pub enum ProblemCategory {
12 Algebraic,
13 Formula,
14 Graph,
15 Misc,
16 Set,
17}
18
19impl ProblemCategory {
20 pub const ALL: [Self; 5] = [
21 Self::Algebraic,
22 Self::Formula,
23 Self::Graph,
24 Self::Misc,
25 Self::Set,
26 ];
27
28 pub const fn as_str(self) -> &'static str {
29 match self {
30 Self::Algebraic => "algebraic",
31 Self::Formula => "formula",
32 Self::Graph => "graph",
33 Self::Misc => "misc",
34 Self::Set => "set",
35 }
36 }
37}
38
39impl fmt::Display for ProblemCategory {
40 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
41 formatter.write_str(self.as_str())
42 }
43}
44
45#[derive(Debug, Clone, PartialEq, Eq)]
47pub struct ParseProblemCategoryError(String);
48
49impl fmt::Display for ParseProblemCategoryError {
50 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
51 let expected = ProblemCategory::ALL.map(ProblemCategory::as_str).join(", ");
52 write!(
53 formatter,
54 "unknown problem category `{}`; expected one of: {expected}",
55 self.0,
56 )
57 }
58}
59
60impl std::error::Error for ParseProblemCategoryError {}
61
62impl FromStr for ProblemCategory {
63 type Err = ParseProblemCategoryError;
64
65 fn from_str(value: &str) -> Result<Self, Self::Err> {
66 Self::ALL
67 .into_iter()
68 .find(|category| category.as_str() == value)
69 .ok_or_else(|| ParseProblemCategoryError(value.to_string()))
70 }
71}
72
73#[derive(Debug, Clone, PartialEq, Eq)]
78pub struct VariantDimension {
79 pub key: &'static str,
81 pub default_value: &'static str,
83 pub allowed_values: &'static [&'static str],
85}
86
87impl VariantDimension {
88 pub const fn new(
90 key: &'static str,
91 default_value: &'static str,
92 allowed_values: &'static [&'static str],
93 ) -> Self {
94 Self {
95 key,
96 default_value,
97 allowed_values,
98 }
99 }
100}
101
102pub struct ProblemSchemaEntry {
120 pub name: &'static str,
122 pub display_name: &'static str,
124 pub aliases: &'static [&'static str],
126 pub dimensions: &'static [VariantDimension],
128 pub category: ProblemCategory,
130 pub module_path: &'static str,
132 pub description: &'static str,
134 pub fields: &'static [FieldInfo],
136}
137
138inventory::collect!(ProblemSchemaEntry);
139
140#[derive(Debug, Clone, Serialize)]
142pub struct ProblemSchemaJson {
143 pub name: String,
145 pub description: String,
147 pub category: ProblemCategory,
149 pub fields: Vec<FieldInfoJson>,
151}
152
153#[derive(Debug, Clone, Serialize)]
155pub struct FieldInfoJson {
156 pub name: String,
158 pub type_name: String,
160 pub description: String,
162}
163
164pub fn collect_schemas() -> Vec<ProblemSchemaJson> {
166 let mut schemas: Vec<ProblemSchemaJson> = inventory::iter::<ProblemSchemaEntry>
167 .into_iter()
168 .map(|entry| ProblemSchemaJson {
169 name: entry.name.to_string(),
170 description: entry.description.to_string(),
171 category: entry.category,
172 fields: entry
173 .fields
174 .iter()
175 .map(|f| FieldInfoJson {
176 name: f.name.to_string(),
177 type_name: f.type_name.to_string(),
178 description: f.description.to_string(),
179 })
180 .collect(),
181 })
182 .collect();
183 schemas.sort_by(|a, b| a.name.cmp(&b.name));
184 schemas
185}
186
187#[cfg(test)]
188#[path = "../unit_tests/registry/schema.rs"]
189mod tests;