headless_lms_server/programs/doc_file_generator/
mod.rs1#![allow(clippy::redundant_clone)]
109#![allow(unused_imports)]
110
111pub mod example;
112
113use chrono::{TimeZone, Utc};
114use example::Example;
115use headless_lms_models::{
116 course_background_question_answers::CourseBackgroundQuestionAnswer,
117 course_background_questions::{
118 CourseBackgroundQuestion, CourseBackgroundQuestionType, CourseBackgroundQuestionsAndAnswers,
119 },
120 course_instance_enrollments::CourseInstanceEnrollmentsInfo,
121 course_module_completions::{
122 CourseModuleCompletion, CourseModuleCompletionWithRegistrationInfo,
123 StudyRegistryCompletion, StudyRegistryGrade,
124 },
125 courses::CourseBreadcrumbInfo,
126 exercise_task_submissions::PeerOrSelfReviewsReceived,
127 exercises::ExerciseStatusSummaryForUser,
128 library::global_stats::{GlobalCourseModuleStatEntry, GlobalStatEntry},
129 page_audio_files::PageAudioFile,
130 page_visit_datum_summary_by_courses::PageVisitDatumSummaryByCourse,
131 page_visit_datum_summary_by_courses_countries::PageVisitDatumSummaryByCoursesCountries,
132 page_visit_datum_summary_by_courses_device_types::PageVisitDatumSummaryByCourseDeviceTypes,
133 page_visit_datum_summary_by_pages::PageVisitDatumSummaryByPages,
134 peer_or_self_review_configs::CourseMaterialPeerOrSelfReviewConfig,
135 peer_or_self_review_question_submissions::{
136 PeerOrSelfReviewAnswer, PeerOrSelfReviewQuestionAndAnswer,
137 PeerOrSelfReviewQuestionSubmission,
138 },
139 peer_or_self_review_submissions::PeerOrSelfReviewSubmission,
140 peer_review_queue_entries::PeerReviewQueueEntry,
141 proposed_block_edits::EditedBlockStillExistsData,
142 research_forms::{ResearchForm, ResearchFormQuestion, ResearchFormQuestionAnswer},
143 student_countries::StudentCountry,
144 teacher_grading_decisions::{TeacherDecisionType, TeacherGradingDecision},
145 user_details::UserDetail,
146 user_research_consents::UserResearchConsent,
147};
148use serde::Serialize;
149use serde_json::{Serializer, Value, json, ser::PrettyFormatter};
150use std::{collections::HashMap, fs};
151#[cfg(feature = "ts_rs")]
152use ts_rs::TS;
153use uuid::Uuid;
154
155use crate::controllers::course_material::exercises::CourseMaterialPeerOrSelfReviewDataWithToken;
156
157fn ex<T: Example>() -> T {
159 Example::example()
160}
161
162#[macro_export]
163macro_rules! doc_path {
164 ($filename:expr_2021, $extension:expr_2021) => {{
165 let windows_safe_filename = $filename
166 .replace('<', "(")
167 .replace('>', ")")
168 .replace(" ", "");
169
170 let mut s = String::new();
171 s.push_str(env!("CARGO_MANIFEST_DIR"));
172 s.push_str("/generated-docs/");
173 s.push_str(windows_safe_filename.as_str());
174 s.push_str($extension);
175 s
176 }};
177}
178
179#[macro_export]
182macro_rules! doc {
183 (T, Option, Vec, $($t:tt)*) => {
184 ::doc_macro::example!($($t)*);
185 doc!(@inner T, $($t)*);
186 doc!(@inner Option, $($t)*);
187 doc!(@inner Vec, $($t)*);
188 };
189 (Option, Vec, $($t:tt)*) => {
190 ::doc_macro::example!($($t)*);
191 doc!(@inner Option, $($t)*);
192 doc!(@inner Vec, $($t)*);
193 };
194 (T, Option, $($t:tt)*) => {
195 ::doc_macro::example!($($t)*);
196 doc!(@inner T, $($t)*);
197 doc!(@inner Option, $($t)*);
198 };
199 (T, Vec, $($t:tt)*) => {
200 ::doc_macro::example!($($t)*);
201 doc!(@inner T, $($t)*);
202 doc!(@inner Vec, $($t)*);
203 };
204 (T, $($t:tt)*) => {
205 ::doc_macro::example!($($t)*);
206 doc!(@inner T, $($t)*);
207 };
208 (Option, $($t:tt)*) => {
209 ::doc_macro::example!($($t)*);
210 doc!(@inner Option, $($t)*);
211 };
212 (Vec, $($t:tt)*) => {
213 ::doc_macro::example!($($t)*);
214 doc!(@inner Vec, $($t)*);
215 };
216 (@inner T, $i:ident :: $($t:tt)*) => {
218 doc!($i, Example::example());
219 };
220 (@inner Option, $i:ident :: $($t:tt)*) => {
221 doc!(Option<$i>, Example::example());
222 };
223 (@inner Vec, $i:ident :: $($t:tt)*) => {
224 doc!(Vec<$i>, Example::example());
225 };
226 (@inner T, $i:ident $($t:tt)*) => {
228 doc!($i, Example::example());
229 };
230 (@inner Option, $i:ident $($t:tt)*) => {
231 doc!(Option<$i>, Example::example());
232 };
233 (@inner Vec, $i:ident $($t:tt)*) => {
234 doc!(Vec<$i>, Example::example());
235 };
236 ($t:ty, $e:expr_2021) => {{
238 let expr: $t = $e;
239
240 let json_path = $crate::doc_path!(
241 stringify!($t),
242 ".json"
243 );
244 $crate::programs::doc_file_generator::write_json(&json_path, expr);
245
246 #[cfg(feature = "ts_rs")]
247 {
248 let ts_path = $crate::doc_path!(
249 stringify!($t),
250 ".ts"
251 );
252
253 $crate::programs::doc_file_generator::write_ts::<$t>(&ts_path, stringify!($t));
254 }
255 }};
256 ($($t:tt)*) => {
258 doc!(T, $($t)*);
259 };
260}
261
262pub async fn main() -> anyhow::Result<()> {
263 fs::read_dir(concat!(env!("CARGO_MANIFEST_DIR"), "/generated-docs/"))
265 .unwrap()
266 .filter_map(|file| {
267 file.ok().filter(|f| {
268 f.file_name()
269 .to_str()
270 .is_some_and(|n| n.ends_with(".json") || n.ends_with(".ts"))
271 })
272 })
273 .for_each(|f| fs::remove_file(f.path()).unwrap());
274
275 controllers();
277
278 external();
279
280 doc!((), ex());
281 doc!(i64, 123);
282 doc!(bool, ex());
283 doc!(
284 Vec<bool>,
285 vec![false, true, false, true, false, true, true, true]
286 );
287 doc!(String, ex());
288 doc!(Uuid, ex());
289 doc!(Vec<Uuid>, ex());
290
291 Ok(())
292}
293
294pub fn write_json<T: Serialize>(path: &str, value: T) {
295 let mut file = std::fs::File::create(path).unwrap();
296 let formatter = PrettyFormatter::with_indent(b" ");
297 let mut serializer = Serializer::with_formatter(&mut file, formatter);
298 serde::Serialize::serialize(&value, &mut serializer).unwrap();
299}
300
301#[cfg(feature = "ts_rs")]
302pub fn write_ts<T: TS>(path: &str, type_name: &str) {
303 let contents = format!("type {} = {}", type_name, T::inline());
304 std::fs::write(path, contents).unwrap();
305}
306
307#[allow(non_local_definitions)]
308fn controllers() {
309 doc!(
310 T,
311 Vec,
312 StudyRegistryCompletion {
313 completion_date: Utc.with_ymd_and_hms(2022, 6, 21, 0, 0, 0).unwrap(),
314 completion_language: "en-US".to_string(),
315 completion_registration_attempt_date: None,
316 email: "student@example.com".to_string(),
317 grade: StudyRegistryGrade::new(true, Some(4)),
318 id: Uuid::parse_str("633852ce-c82a-4d60-8ab5-28745163f6f9").unwrap(),
319 user_id,
320 tier: None
321 }
322 );
323}
324
325fn external() {
327 std::fs::write(doc_path!("Bytes", ".ts"), "type Bytes = Blob").unwrap();
328}