1use serde::{Deserialize, Serialize};
2use std::collections::{HashMap, HashSet};
3
4use crate::api_data_structures::{TraitInfo, TypeInfo};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct TutorialSystem {
8 pub tutorials: Vec<Tutorial>,
9 pub learning_paths: Vec<LearningPath>,
10 pub progress_tracker: ProgressTracker,
11 pub assessment_engine: AssessmentEngine,
12 pub config: TutorialConfig,
13}
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct TutorialConfig {
17 pub difficulty_levels: Vec<DifficultyLevel>,
18 pub enable_interactive_examples: bool,
19 pub enable_progress_tracking: bool,
20 pub enable_assessments: bool,
21 pub max_tutorial_duration: u32,
22 pub code_execution_timeout: u32,
23 pub personalization_enabled: bool,
24}
25
26impl Default for TutorialConfig {
27 fn default() -> Self {
28 Self {
29 difficulty_levels: vec![
30 DifficultyLevel::Beginner,
31 DifficultyLevel::Intermediate,
32 DifficultyLevel::Advanced,
33 ],
34 enable_interactive_examples: true,
35 enable_progress_tracking: true,
36 enable_assessments: true,
37 max_tutorial_duration: 3600,
38 code_execution_timeout: 30,
39 personalization_enabled: true,
40 }
41 }
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
45pub enum DifficultyLevel {
46 Beginner,
47 Intermediate,
48 Advanced,
49 Expert,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct Tutorial {
54 pub id: String,
55 pub title: String,
56 pub description: String,
57 pub difficulty: DifficultyLevel,
58 pub duration_minutes: u32,
59 pub prerequisites: Vec<String>,
60 pub learning_objectives: Vec<String>,
61 pub sections: Vec<TutorialSection>,
62 pub assessment: Option<Assessment>,
63 pub metadata: TutorialMetadata,
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct TutorialMetadata {
68 pub author: String,
69 pub created_at: chrono::DateTime<chrono::Utc>,
70 pub updated_at: chrono::DateTime<chrono::Utc>,
71 pub version: String,
72 pub tags: Vec<String>,
73 pub category: TutorialCategory,
74 pub language: String,
75 pub popularity_score: f64,
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize)]
79pub enum TutorialCategory {
80 GettingStarted,
81 CoreConcepts,
82 AdvancedFeatures,
83 BestPractices,
84 RealWorldExamples,
85 Performance,
86 Testing,
87 Integration,
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct TutorialSection {
92 pub id: String,
93 pub title: String,
94 pub content: SectionContent,
95 pub interactive_elements: Vec<InteractiveElement>,
96 pub estimated_duration: u32,
97 pub completion_criteria: CompletionCriteria,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize)]
101pub enum SectionContent {
102 Text {
103 content: String,
104 format: ContentFormat,
105 },
106 Code {
107 content: String,
108 language: String,
109 runnable: bool,
110 },
111 Exercise {
112 description: String,
113 starter_code: String,
114 solution: String,
115 },
116 Quiz {
117 questions: Vec<QuizQuestion>,
118 },
119 Video {
120 url: String,
121 duration: u32,
122 transcript: Option<String>,
123 },
124 Interactive {
125 component_type: String,
126 config: serde_json::Value,
127 },
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize)]
131pub enum ContentFormat {
132 Markdown,
133 Html,
134 PlainText,
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct InteractiveElement {
139 pub element_type: InteractiveElementType,
140 pub config: serde_json::Value,
141 pub validation_rules: Vec<ValidationRule>,
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
145pub enum InteractiveElementType {
146 CodeEditor,
147 LiveExample,
148 Quiz,
149 Diagram,
150 Simulation,
151 Playground,
152}
153
154#[derive(Debug, Clone, Serialize, Deserialize)]
155pub struct ValidationRule {
156 pub rule_type: ValidationType,
157 pub condition: String,
158 pub error_message: String,
159 pub hint: Option<String>,
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize)]
163pub enum ValidationType {
164 Compilation,
165 Runtime,
166 Output,
167 Style,
168 Performance,
169}
170
171#[derive(Debug, Clone, Serialize, Deserialize)]
172pub struct CompletionCriteria {
173 pub required_interactions: Vec<String>,
174 pub minimum_score: Option<f64>,
175 pub time_spent_minimum: Option<u32>,
176 pub code_execution_required: bool,
177}
178
179#[derive(Debug, Clone, Serialize, Deserialize)]
180pub struct LearningPath {
181 pub id: String,
182 pub title: String,
183 pub description: String,
184 pub difficulty: DifficultyLevel,
185 pub estimated_hours: u32,
186 pub tutorial_sequence: Vec<String>,
187 pub completion_rewards: Vec<String>,
188 pub prerequisites: Vec<String>,
189}
190
191#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct ProgressTracker {
193 pub user_progress: HashMap<String, UserProgress>,
194 pub global_statistics: GlobalStatistics,
195}
196
197#[derive(Debug, Clone, Serialize, Deserialize)]
198pub struct UserProgress {
199 pub user_id: String,
200 pub completed_tutorials: HashSet<String>,
201 pub current_tutorial: Option<String>,
202 pub current_section: Option<String>,
203 pub completion_percentage: f64,
204 pub time_spent: u32,
205 pub assessment_scores: HashMap<String, f64>,
206 pub achievements: Vec<Achievement>,
207 pub learning_preferences: LearningPreferences,
208}
209
210#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct GlobalStatistics {
212 pub total_users: usize,
213 pub tutorial_completion_rates: HashMap<String, f64>,
214 pub average_scores: HashMap<String, f64>,
215 pub popular_tutorials: Vec<String>,
216 pub common_challenges: Vec<String>,
217}
218
219#[derive(Debug, Clone, Serialize, Deserialize)]
220pub struct Achievement {
221 pub id: String,
222 pub title: String,
223 pub description: String,
224 pub icon: String,
225 pub earned_at: chrono::DateTime<chrono::Utc>,
226 pub rarity: AchievementRarity,
227}
228
229#[derive(Debug, Clone, Serialize, Deserialize)]
230pub enum AchievementRarity {
231 Common,
232 Uncommon,
233 Rare,
234 Epic,
235 Legendary,
236}
237
238#[derive(Debug, Clone, Serialize, Deserialize)]
239pub struct LearningPreferences {
240 pub preferred_difficulty: DifficultyLevel,
241 pub learning_pace: LearningPace,
242 pub content_types: Vec<ContentType>,
243 pub reminder_frequency: ReminderFrequency,
244}
245
246#[derive(Debug, Clone, Serialize, Deserialize)]
247pub enum LearningPace {
248 Slow,
249 Normal,
250 Fast,
251 SelfPaced,
252}
253
254#[derive(Debug, Clone, Serialize, Deserialize)]
255pub enum ContentType {
256 Text,
257 Video,
258 Interactive,
259 Code,
260 Exercises,
261}
262
263#[derive(Debug, Clone, Serialize, Deserialize)]
264pub enum ReminderFrequency {
265 Never,
266 Daily,
267 Weekly,
268 Monthly,
269}
270
271#[derive(Debug, Clone, Serialize, Deserialize)]
272pub struct AssessmentEngine {
273 pub assessments: HashMap<String, Assessment>,
274 pub question_bank: Vec<QuizQuestion>,
275 pub scoring_algorithms: HashMap<String, ScoringAlgorithm>,
276}
277
278#[derive(Debug, Clone, Serialize, Deserialize)]
279pub struct Assessment {
280 pub id: String,
281 pub title: String,
282 pub description: String,
283 pub questions: Vec<String>,
284 pub time_limit: Option<u32>,
285 pub passing_score: f64,
286 pub max_attempts: Option<u32>,
287 pub feedback_mode: FeedbackMode,
288}
289
290#[derive(Debug, Clone, Serialize, Deserialize)]
291pub enum FeedbackMode {
292 Immediate,
293 EndOfAssessment,
294 Manual,
295}
296
297#[derive(Debug, Clone, Serialize, Deserialize)]
298pub struct QuizQuestion {
299 pub id: String,
300 pub question_type: QuestionType,
301 pub content: String,
302 pub options: Vec<String>,
303 pub correct_answer: String,
304 pub explanation: String,
305 pub difficulty: DifficultyLevel,
306 pub tags: Vec<String>,
307 pub points: u32,
308}
309
310#[derive(Debug, Clone, Serialize, Deserialize)]
311pub enum QuestionType {
312 MultipleChoice,
313 TrueFalse,
314 ShortAnswer,
315 CodeCompletion,
316 CodeReview,
317 Matching,
318}
319
320#[derive(Debug, Clone, Serialize, Deserialize)]
321pub struct ScoringAlgorithm {
322 pub name: String,
323 pub description: String,
324 pub formula: String,
325 pub parameters: HashMap<String, f64>,
326}
327
328pub struct TutorialBuilder {
329 tutorial: Tutorial,
330 current_section: Option<TutorialSection>,
331}
332
333impl TutorialBuilder {
334 pub fn new(id: String, title: String) -> Self {
335 Self {
336 tutorial: Tutorial {
337 id,
338 title,
339 description: String::new(),
340 difficulty: DifficultyLevel::Beginner,
341 duration_minutes: 0,
342 prerequisites: Vec::new(),
343 learning_objectives: Vec::new(),
344 sections: Vec::new(),
345 assessment: None,
346 metadata: TutorialMetadata {
347 author: String::new(),
348 created_at: chrono::Utc::now(),
349 updated_at: chrono::Utc::now(),
350 version: "1.0.0".to_string(),
351 tags: Vec::new(),
352 category: TutorialCategory::GettingStarted,
353 language: "en".to_string(),
354 popularity_score: 0.0,
355 },
356 },
357 current_section: None,
358 }
359 }
360
361 pub fn description(mut self, description: String) -> Self {
362 self.tutorial.description = description;
363 self
364 }
365
366 pub fn difficulty(mut self, difficulty: DifficultyLevel) -> Self {
367 self.tutorial.difficulty = difficulty;
368 self
369 }
370
371 pub fn duration(mut self, minutes: u32) -> Self {
372 self.tutorial.duration_minutes = minutes;
373 self
374 }
375
376 pub fn prerequisite(mut self, prerequisite: String) -> Self {
377 self.tutorial.prerequisites.push(prerequisite);
378 self
379 }
380
381 pub fn learning_objective(mut self, objective: String) -> Self {
382 self.tutorial.learning_objectives.push(objective);
383 self
384 }
385
386 pub fn author(mut self, author: String) -> Self {
387 self.tutorial.metadata.author = author;
388 self
389 }
390
391 pub fn category(mut self, category: TutorialCategory) -> Self {
392 self.tutorial.metadata.category = category;
393 self
394 }
395
396 pub fn tag(mut self, tag: String) -> Self {
397 self.tutorial.metadata.tags.push(tag);
398 self
399 }
400
401 pub fn section(mut self, id: String, title: String) -> Self {
402 if let Some(section) = self.current_section.take() {
403 self.tutorial.sections.push(section);
404 }
405
406 self.current_section = Some(TutorialSection {
407 id,
408 title,
409 content: SectionContent::Text {
410 content: String::new(),
411 format: ContentFormat::Markdown,
412 },
413 interactive_elements: Vec::new(),
414 estimated_duration: 0,
415 completion_criteria: CompletionCriteria {
416 required_interactions: Vec::new(),
417 minimum_score: None,
418 time_spent_minimum: None,
419 code_execution_required: false,
420 },
421 });
422
423 self
424 }
425
426 pub fn text_content(mut self, content: String, format: ContentFormat) -> Self {
427 if let Some(ref mut section) = self.current_section {
428 section.content = SectionContent::Text { content, format };
429 }
430 self
431 }
432
433 pub fn code_content(mut self, content: String, language: String, runnable: bool) -> Self {
434 if let Some(ref mut section) = self.current_section {
435 section.content = SectionContent::Code {
436 content,
437 language,
438 runnable,
439 };
440 }
441 self
442 }
443
444 pub fn exercise(mut self, description: String, starter_code: String, solution: String) -> Self {
445 if let Some(ref mut section) = self.current_section {
446 section.content = SectionContent::Exercise {
447 description,
448 starter_code,
449 solution,
450 };
451 }
452 self
453 }
454
455 pub fn interactive_element(
456 mut self,
457 element_type: InteractiveElementType,
458 config: serde_json::Value,
459 ) -> Self {
460 if let Some(ref mut section) = self.current_section {
461 section.interactive_elements.push(InteractiveElement {
462 element_type,
463 config,
464 validation_rules: Vec::new(),
465 });
466 }
467 self
468 }
469
470 pub fn section_duration(mut self, minutes: u32) -> Self {
471 if let Some(ref mut section) = self.current_section {
472 section.estimated_duration = minutes;
473 }
474 self
475 }
476
477 pub fn completion_criteria(mut self, criteria: CompletionCriteria) -> Self {
478 if let Some(ref mut section) = self.current_section {
479 section.completion_criteria = criteria;
480 }
481 self
482 }
483
484 pub fn assessment(mut self, assessment: Assessment) -> Self {
485 self.tutorial.assessment = Some(assessment);
486 self
487 }
488
489 pub fn build(mut self) -> Tutorial {
490 if let Some(section) = self.current_section.take() {
491 self.tutorial.sections.push(section);
492 }
493 self.tutorial.metadata.updated_at = chrono::Utc::now();
494 self.tutorial
495 }
496}
497
498impl TutorialSystem {
499 pub fn new(config: TutorialConfig) -> Self {
500 Self {
501 tutorials: Vec::new(),
502 learning_paths: Vec::new(),
503 progress_tracker: ProgressTracker {
504 user_progress: HashMap::new(),
505 global_statistics: GlobalStatistics {
506 total_users: 0,
507 tutorial_completion_rates: HashMap::new(),
508 average_scores: HashMap::new(),
509 popular_tutorials: Vec::new(),
510 common_challenges: Vec::new(),
511 },
512 },
513 assessment_engine: AssessmentEngine {
514 assessments: HashMap::new(),
515 question_bank: Vec::new(),
516 scoring_algorithms: HashMap::new(),
517 },
518 config,
519 }
520 }
521
522 pub fn add_tutorial(&mut self, tutorial: Tutorial) {
523 self.tutorials.push(tutorial);
524 }
525
526 pub fn create_learning_path(&mut self, path: LearningPath) {
527 self.learning_paths.push(path);
528 }
529
530 pub fn generate_tutorial_from_trait(&self, trait_info: &TraitInfo) -> Tutorial {
531 TutorialBuilder::new(
532 format!("trait_{}", trait_info.name),
533 format!("Understanding the {} Trait", trait_info.name)
534 )
535 .description(format!("Learn how to use and implement the {} trait in your Rust code", trait_info.name))
536 .difficulty(DifficultyLevel::Intermediate)
537 .duration(30)
538 .learning_objective(format!("Understand the purpose and usage of {}", trait_info.name))
539 .learning_objective("Implement the trait in custom types".to_string())
540 .learning_objective("Use trait methods effectively".to_string())
541 .category(TutorialCategory::CoreConcepts)
542 .tag("traits".to_string())
543 .tag("rust".to_string())
544 .section("introduction".to_string(), "Introduction".to_string())
545 .text_content(
546 format!("The {} trait is a fundamental concept in Rust that allows you to define shared behavior across different types.\n\n{}",
547 trait_info.name, trait_info.description),
548 ContentFormat::Markdown
549 )
550 .section_duration(5)
551 .section("implementation".to_string(), "Implementation Guide".to_string())
552 .code_content(
553 self.generate_trait_implementation_example(trait_info),
554 "rust".to_string(),
555 true
556 )
557 .interactive_element(
558 InteractiveElementType::CodeEditor,
559 serde_json::json!({
560 "template": self.generate_trait_template(trait_info),
561 "validation": "compilation"
562 })
563 )
564 .section_duration(15)
565 .section("examples".to_string(), "Practical Examples".to_string())
566 .text_content(
567 "Let's explore some real-world examples of using this trait:".to_string(),
568 ContentFormat::Markdown
569 )
570 .section_duration(10)
571 .build()
572 }
573
574 fn generate_trait_implementation_example(&self, trait_info: &TraitInfo) -> String {
575 format!(
576 "// Example implementation of {}\n\
577 struct MyStruct {{\n \
578 // Your fields here\n\
579 }}\n\n\
580 impl {} for MyStruct {{\n \
581 // Implement required methods\n\
582 }}",
583 trait_info.name, trait_info.name
584 )
585 }
586
587 fn generate_trait_template(&self, trait_info: &TraitInfo) -> String {
588 format!(
589 "// TODO: Implement {} for your custom type\n\
590 struct YourType {{\n \
591 // Add your fields\n\
592 }}\n\n\
593 impl {} for YourType {{\n \
594 // Implement the required methods\n\
595 }}",
596 trait_info.name, trait_info.name
597 )
598 }
599
600 pub fn generate_tutorial_from_type(&self, type_info: &TypeInfo) -> Tutorial {
601 TutorialBuilder::new(
602 format!("type_{}", type_info.name),
603 format!("Working with {} Type", type_info.name),
604 )
605 .description(format!(
606 "Learn how to use the {} type effectively",
607 type_info.name
608 ))
609 .difficulty(DifficultyLevel::Beginner)
610 .duration(20)
611 .learning_objective(format!("Understand the {} type", type_info.name))
612 .learning_objective("Create and manipulate instances".to_string())
613 .category(TutorialCategory::CoreConcepts)
614 .tag("types".to_string())
615 .tag("rust".to_string())
616 .section("overview".to_string(), "Type Overview".to_string())
617 .text_content(
618 format!(
619 "{}\n\n**Type signature**: `{:?}`",
620 type_info.description, type_info.kind
621 ),
622 ContentFormat::Markdown,
623 )
624 .section_duration(10)
625 .section("usage".to_string(), "Usage Examples".to_string())
626 .code_content(
627 self.generate_type_usage_example(type_info),
628 "rust".to_string(),
629 true,
630 )
631 .section_duration(10)
632 .build()
633 }
634
635 fn generate_type_usage_example(&self, type_info: &TypeInfo) -> String {
636 format!(
637 "// Example usage of {}\n\
638 let instance = {}::new();\n\
639 // Use the instance...",
640 type_info.name, type_info.name
641 )
642 }
643
644 pub fn get_recommended_tutorials(&self, user_id: &str) -> Vec<&Tutorial> {
645 if let Some(progress) = self.progress_tracker.user_progress.get(user_id) {
646 self.tutorials
647 .iter()
648 .filter(|tutorial| {
649 !progress.completed_tutorials.contains(&tutorial.id)
650 && self.meets_prerequisites(tutorial, progress)
651 })
652 .collect()
653 } else {
654 self.tutorials
655 .iter()
656 .filter(|tutorial| tutorial.prerequisites.is_empty())
657 .collect()
658 }
659 }
660
661 fn meets_prerequisites(&self, tutorial: &Tutorial, progress: &UserProgress) -> bool {
662 tutorial
663 .prerequisites
664 .iter()
665 .all(|prereq| progress.completed_tutorials.contains(prereq))
666 }
667
668 pub fn start_tutorial(&mut self, user_id: String, tutorial_id: String) -> Result<(), String> {
669 if !self.tutorials.iter().any(|t| t.id == tutorial_id) {
670 return Err("Tutorial not found".to_string());
671 }
672
673 let user_id_clone = user_id.clone();
674 let progress = self
675 .progress_tracker
676 .user_progress
677 .entry(user_id)
678 .or_insert_with(|| UserProgress {
679 user_id: user_id_clone,
680 completed_tutorials: HashSet::new(),
681 current_tutorial: None,
682 current_section: None,
683 completion_percentage: 0.0,
684 time_spent: 0,
685 assessment_scores: HashMap::new(),
686 achievements: Vec::new(),
687 learning_preferences: LearningPreferences {
688 preferred_difficulty: DifficultyLevel::Beginner,
689 learning_pace: LearningPace::Normal,
690 content_types: vec![ContentType::Text, ContentType::Code],
691 reminder_frequency: ReminderFrequency::Weekly,
692 },
693 });
694
695 progress.current_tutorial = Some(tutorial_id);
696 progress.current_section = None;
697 progress.completion_percentage = 0.0;
698
699 Ok(())
700 }
701
702 pub fn complete_section(
703 &mut self,
704 user_id: &str,
705 tutorial_id: &str,
706 _section_id: &str,
707 ) -> Result<(), String> {
708 let progress = self
709 .progress_tracker
710 .user_progress
711 .get_mut(user_id)
712 .ok_or("User not found")?;
713
714 if progress.current_tutorial.as_ref() != Some(&tutorial_id.to_string()) {
715 return Err("Tutorial not in progress".to_string());
716 }
717
718 let tutorial_info = self
719 .tutorials
720 .iter()
721 .find(|t| t.id == tutorial_id)
722 .map(|t| (t.sections.len(), t.title.clone(), t.difficulty.clone()));
723
724 if let Some((total_sections, title, difficulty)) = tutorial_info {
725 let completed_sections = progress.completed_tutorials.len();
726 progress.completion_percentage =
727 (completed_sections as f64 / total_sections as f64) * 100.0;
728
729 if progress.completion_percentage >= 100.0 {
730 progress.completed_tutorials.insert(tutorial_id.to_string());
731 progress.current_tutorial = None;
732 progress.current_section = None;
733
734 self.award_completion_achievement_info(user_id, tutorial_id, &title, &difficulty);
735 }
736 }
737
738 Ok(())
739 }
740
741 fn award_completion_achievement_info(
742 &mut self,
743 user_id: &str,
744 tutorial_id: &str,
745 title: &str,
746 difficulty: &DifficultyLevel,
747 ) {
748 if let Some(progress) = self.progress_tracker.user_progress.get_mut(user_id) {
749 let achievement = Achievement {
750 id: format!("completed_{}", tutorial_id),
751 title: format!("Completed: {}", title),
752 description: format!("Successfully completed the {} tutorial", title),
753 icon: "🎓".to_string(),
754 earned_at: chrono::Utc::now(),
755 rarity: match difficulty {
756 DifficultyLevel::Beginner => AchievementRarity::Common,
757 DifficultyLevel::Intermediate => AchievementRarity::Uncommon,
758 DifficultyLevel::Advanced => AchievementRarity::Rare,
759 DifficultyLevel::Expert => AchievementRarity::Epic,
760 },
761 };
762 progress.achievements.push(achievement);
763 }
764 }
765
766 pub fn get_user_progress(&self, user_id: &str) -> Option<&UserProgress> {
767 self.progress_tracker.user_progress.get(user_id)
768 }
769
770 pub fn update_global_statistics(&mut self) {
771 self.progress_tracker.global_statistics.total_users =
772 self.progress_tracker.user_progress.len();
773
774 for tutorial in &self.tutorials {
775 let completion_count = self
776 .progress_tracker
777 .user_progress
778 .values()
779 .filter(|progress| progress.completed_tutorials.contains(&tutorial.id))
780 .count();
781
782 let completion_rate = if self.progress_tracker.global_statistics.total_users > 0 {
783 completion_count as f64 / self.progress_tracker.global_statistics.total_users as f64
784 } else {
785 0.0
786 };
787
788 self.progress_tracker
789 .global_statistics
790 .tutorial_completion_rates
791 .insert(tutorial.id.clone(), completion_rate);
792 }
793 }
794}
795
796#[allow(non_snake_case)]
797#[cfg(test)]
798mod tests {
799 use super::*;
800
801 #[test]
802 fn test_tutorial_builder() {
803 let tutorial = TutorialBuilder::new("test".to_string(), "Test Tutorial".to_string())
804 .description("Test description".to_string())
805 .difficulty(DifficultyLevel::Intermediate)
806 .duration(60)
807 .author("Test Author".to_string())
808 .build();
809
810 assert_eq!(tutorial.id, "test");
811 assert_eq!(tutorial.title, "Test Tutorial");
812 assert_eq!(tutorial.description, "Test description");
813 assert_eq!(tutorial.duration_minutes, 60);
814 assert!(matches!(tutorial.difficulty, DifficultyLevel::Intermediate));
815 }
816
817 #[test]
818 fn test_tutorial_system_creation() {
819 let config = TutorialConfig::default();
820 let system = TutorialSystem::new(config);
821 assert_eq!(system.tutorials.len(), 0);
822 assert_eq!(system.learning_paths.len(), 0);
823 }
824
825 #[test]
826 fn test_user_progress_tracking() {
827 let mut system = TutorialSystem::new(TutorialConfig::default());
828 let tutorial = TutorialBuilder::new("test".to_string(), "Test".to_string()).build();
829 system.add_tutorial(tutorial);
830
831 let result = system.start_tutorial("user1".to_string(), "test".to_string());
832 assert!(result.is_ok());
833
834 let progress = system.get_user_progress("user1");
835 assert!(progress.is_some());
836 assert_eq!(progress.unwrap().current_tutorial, Some("test".to_string()));
837 }
838
839 #[test]
840 fn test_section_completion() {
841 let mut system = TutorialSystem::new(TutorialConfig::default());
842 let tutorial = TutorialBuilder::new("test".to_string(), "Test".to_string())
843 .section("section1".to_string(), "Section 1".to_string())
844 .build();
845 system.add_tutorial(tutorial);
846
847 system
848 .start_tutorial("user1".to_string(), "test".to_string())
849 .unwrap();
850 let result = system.complete_section("user1", "test", "section1");
851 assert!(result.is_ok());
852 }
853
854 #[test]
855 fn test_tutorial_generation_from_trait() {
856 let system = TutorialSystem::new(TutorialConfig::default());
857 let trait_info = TraitInfo {
858 name: "Display".to_string(),
859 path: "std::fmt::Display".to_string(),
860 description: "Trait for formatting output".to_string(),
861 methods: vec![],
862 associated_types: vec![],
863 generics: vec![],
864 supertraits: vec![],
865 implementations: vec![],
866 };
867
868 let tutorial = system.generate_tutorial_from_trait(&trait_info);
869 assert_eq!(tutorial.id, "trait_Display");
870 assert!(tutorial.title.contains("Display"));
871 assert!(!tutorial.sections.is_empty());
872 }
873}