use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use crate::api_data_structures::{TraitInfo, TypeInfo};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TutorialSystem {
pub tutorials: Vec<Tutorial>,
pub learning_paths: Vec<LearningPath>,
pub progress_tracker: ProgressTracker,
pub assessment_engine: AssessmentEngine,
pub config: TutorialConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TutorialConfig {
pub difficulty_levels: Vec<DifficultyLevel>,
pub enable_interactive_examples: bool,
pub enable_progress_tracking: bool,
pub enable_assessments: bool,
pub max_tutorial_duration: u32,
pub code_execution_timeout: u32,
pub personalization_enabled: bool,
}
impl Default for TutorialConfig {
fn default() -> Self {
Self {
difficulty_levels: vec![
DifficultyLevel::Beginner,
DifficultyLevel::Intermediate,
DifficultyLevel::Advanced,
],
enable_interactive_examples: true,
enable_progress_tracking: true,
enable_assessments: true,
max_tutorial_duration: 3600,
code_execution_timeout: 30,
personalization_enabled: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum DifficultyLevel {
Beginner,
Intermediate,
Advanced,
Expert,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Tutorial {
pub id: String,
pub title: String,
pub description: String,
pub difficulty: DifficultyLevel,
pub duration_minutes: u32,
pub prerequisites: Vec<String>,
pub learning_objectives: Vec<String>,
pub sections: Vec<TutorialSection>,
pub assessment: Option<Assessment>,
pub metadata: TutorialMetadata,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TutorialMetadata {
pub author: String,
pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>,
pub version: String,
pub tags: Vec<String>,
pub category: TutorialCategory,
pub language: String,
pub popularity_score: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TutorialCategory {
GettingStarted,
CoreConcepts,
AdvancedFeatures,
BestPractices,
RealWorldExamples,
Performance,
Testing,
Integration,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TutorialSection {
pub id: String,
pub title: String,
pub content: SectionContent,
pub interactive_elements: Vec<InteractiveElement>,
pub estimated_duration: u32,
pub completion_criteria: CompletionCriteria,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SectionContent {
Text {
content: String,
format: ContentFormat,
},
Code {
content: String,
language: String,
runnable: bool,
},
Exercise {
description: String,
starter_code: String,
solution: String,
},
Quiz {
questions: Vec<QuizQuestion>,
},
Video {
url: String,
duration: u32,
transcript: Option<String>,
},
Interactive {
component_type: String,
config: serde_json::Value,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ContentFormat {
Markdown,
Html,
PlainText,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InteractiveElement {
pub element_type: InteractiveElementType,
pub config: serde_json::Value,
pub validation_rules: Vec<ValidationRule>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum InteractiveElementType {
CodeEditor,
LiveExample,
Quiz,
Diagram,
Simulation,
Playground,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationRule {
pub rule_type: ValidationType,
pub condition: String,
pub error_message: String,
pub hint: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ValidationType {
Compilation,
Runtime,
Output,
Style,
Performance,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompletionCriteria {
pub required_interactions: Vec<String>,
pub minimum_score: Option<f64>,
pub time_spent_minimum: Option<u32>,
pub code_execution_required: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LearningPath {
pub id: String,
pub title: String,
pub description: String,
pub difficulty: DifficultyLevel,
pub estimated_hours: u32,
pub tutorial_sequence: Vec<String>,
pub completion_rewards: Vec<String>,
pub prerequisites: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProgressTracker {
pub user_progress: HashMap<String, UserProgress>,
pub global_statistics: GlobalStatistics,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserProgress {
pub user_id: String,
pub completed_tutorials: HashSet<String>,
pub current_tutorial: Option<String>,
pub current_section: Option<String>,
pub completion_percentage: f64,
pub time_spent: u32,
pub assessment_scores: HashMap<String, f64>,
pub achievements: Vec<Achievement>,
pub learning_preferences: LearningPreferences,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GlobalStatistics {
pub total_users: usize,
pub tutorial_completion_rates: HashMap<String, f64>,
pub average_scores: HashMap<String, f64>,
pub popular_tutorials: Vec<String>,
pub common_challenges: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Achievement {
pub id: String,
pub title: String,
pub description: String,
pub icon: String,
pub earned_at: chrono::DateTime<chrono::Utc>,
pub rarity: AchievementRarity,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AchievementRarity {
Common,
Uncommon,
Rare,
Epic,
Legendary,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LearningPreferences {
pub preferred_difficulty: DifficultyLevel,
pub learning_pace: LearningPace,
pub content_types: Vec<ContentType>,
pub reminder_frequency: ReminderFrequency,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum LearningPace {
Slow,
Normal,
Fast,
SelfPaced,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ContentType {
Text,
Video,
Interactive,
Code,
Exercises,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ReminderFrequency {
Never,
Daily,
Weekly,
Monthly,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssessmentEngine {
pub assessments: HashMap<String, Assessment>,
pub question_bank: Vec<QuizQuestion>,
pub scoring_algorithms: HashMap<String, ScoringAlgorithm>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Assessment {
pub id: String,
pub title: String,
pub description: String,
pub questions: Vec<String>,
pub time_limit: Option<u32>,
pub passing_score: f64,
pub max_attempts: Option<u32>,
pub feedback_mode: FeedbackMode,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum FeedbackMode {
Immediate,
EndOfAssessment,
Manual,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QuizQuestion {
pub id: String,
pub question_type: QuestionType,
pub content: String,
pub options: Vec<String>,
pub correct_answer: String,
pub explanation: String,
pub difficulty: DifficultyLevel,
pub tags: Vec<String>,
pub points: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum QuestionType {
MultipleChoice,
TrueFalse,
ShortAnswer,
CodeCompletion,
CodeReview,
Matching,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScoringAlgorithm {
pub name: String,
pub description: String,
pub formula: String,
pub parameters: HashMap<String, f64>,
}
pub struct TutorialBuilder {
tutorial: Tutorial,
current_section: Option<TutorialSection>,
}
impl TutorialBuilder {
pub fn new(id: String, title: String) -> Self {
Self {
tutorial: Tutorial {
id,
title,
description: String::new(),
difficulty: DifficultyLevel::Beginner,
duration_minutes: 0,
prerequisites: Vec::new(),
learning_objectives: Vec::new(),
sections: Vec::new(),
assessment: None,
metadata: TutorialMetadata {
author: String::new(),
created_at: chrono::Utc::now(),
updated_at: chrono::Utc::now(),
version: "1.0.0".to_string(),
tags: Vec::new(),
category: TutorialCategory::GettingStarted,
language: "en".to_string(),
popularity_score: 0.0,
},
},
current_section: None,
}
}
pub fn description(mut self, description: String) -> Self {
self.tutorial.description = description;
self
}
pub fn difficulty(mut self, difficulty: DifficultyLevel) -> Self {
self.tutorial.difficulty = difficulty;
self
}
pub fn duration(mut self, minutes: u32) -> Self {
self.tutorial.duration_minutes = minutes;
self
}
pub fn prerequisite(mut self, prerequisite: String) -> Self {
self.tutorial.prerequisites.push(prerequisite);
self
}
pub fn learning_objective(mut self, objective: String) -> Self {
self.tutorial.learning_objectives.push(objective);
self
}
pub fn author(mut self, author: String) -> Self {
self.tutorial.metadata.author = author;
self
}
pub fn category(mut self, category: TutorialCategory) -> Self {
self.tutorial.metadata.category = category;
self
}
pub fn tag(mut self, tag: String) -> Self {
self.tutorial.metadata.tags.push(tag);
self
}
pub fn section(mut self, id: String, title: String) -> Self {
if let Some(section) = self.current_section.take() {
self.tutorial.sections.push(section);
}
self.current_section = Some(TutorialSection {
id,
title,
content: SectionContent::Text {
content: String::new(),
format: ContentFormat::Markdown,
},
interactive_elements: Vec::new(),
estimated_duration: 0,
completion_criteria: CompletionCriteria {
required_interactions: Vec::new(),
minimum_score: None,
time_spent_minimum: None,
code_execution_required: false,
},
});
self
}
pub fn text_content(mut self, content: String, format: ContentFormat) -> Self {
if let Some(ref mut section) = self.current_section {
section.content = SectionContent::Text { content, format };
}
self
}
pub fn code_content(mut self, content: String, language: String, runnable: bool) -> Self {
if let Some(ref mut section) = self.current_section {
section.content = SectionContent::Code {
content,
language,
runnable,
};
}
self
}
pub fn exercise(mut self, description: String, starter_code: String, solution: String) -> Self {
if let Some(ref mut section) = self.current_section {
section.content = SectionContent::Exercise {
description,
starter_code,
solution,
};
}
self
}
pub fn interactive_element(
mut self,
element_type: InteractiveElementType,
config: serde_json::Value,
) -> Self {
if let Some(ref mut section) = self.current_section {
section.interactive_elements.push(InteractiveElement {
element_type,
config,
validation_rules: Vec::new(),
});
}
self
}
pub fn section_duration(mut self, minutes: u32) -> Self {
if let Some(ref mut section) = self.current_section {
section.estimated_duration = minutes;
}
self
}
pub fn completion_criteria(mut self, criteria: CompletionCriteria) -> Self {
if let Some(ref mut section) = self.current_section {
section.completion_criteria = criteria;
}
self
}
pub fn assessment(mut self, assessment: Assessment) -> Self {
self.tutorial.assessment = Some(assessment);
self
}
pub fn build(mut self) -> Tutorial {
if let Some(section) = self.current_section.take() {
self.tutorial.sections.push(section);
}
self.tutorial.metadata.updated_at = chrono::Utc::now();
self.tutorial
}
}
impl TutorialSystem {
pub fn new(config: TutorialConfig) -> Self {
Self {
tutorials: Vec::new(),
learning_paths: Vec::new(),
progress_tracker: ProgressTracker {
user_progress: HashMap::new(),
global_statistics: GlobalStatistics {
total_users: 0,
tutorial_completion_rates: HashMap::new(),
average_scores: HashMap::new(),
popular_tutorials: Vec::new(),
common_challenges: Vec::new(),
},
},
assessment_engine: AssessmentEngine {
assessments: HashMap::new(),
question_bank: Vec::new(),
scoring_algorithms: HashMap::new(),
},
config,
}
}
pub fn add_tutorial(&mut self, tutorial: Tutorial) {
self.tutorials.push(tutorial);
}
pub fn create_learning_path(&mut self, path: LearningPath) {
self.learning_paths.push(path);
}
pub fn generate_tutorial_from_trait(&self, trait_info: &TraitInfo) -> Tutorial {
TutorialBuilder::new(
format!("trait_{}", trait_info.name),
format!("Understanding the {} Trait", trait_info.name)
)
.description(format!("Learn how to use and implement the {} trait in your Rust code", trait_info.name))
.difficulty(DifficultyLevel::Intermediate)
.duration(30)
.learning_objective(format!("Understand the purpose and usage of {}", trait_info.name))
.learning_objective("Implement the trait in custom types".to_string())
.learning_objective("Use trait methods effectively".to_string())
.category(TutorialCategory::CoreConcepts)
.tag("traits".to_string())
.tag("rust".to_string())
.section("introduction".to_string(), "Introduction".to_string())
.text_content(
format!("The {} trait is a fundamental concept in Rust that allows you to define shared behavior across different types.\n\n{}",
trait_info.name, trait_info.description),
ContentFormat::Markdown
)
.section_duration(5)
.section("implementation".to_string(), "Implementation Guide".to_string())
.code_content(
self.generate_trait_implementation_example(trait_info),
"rust".to_string(),
true
)
.interactive_element(
InteractiveElementType::CodeEditor,
serde_json::json!({
"template": self.generate_trait_template(trait_info),
"validation": "compilation"
})
)
.section_duration(15)
.section("examples".to_string(), "Practical Examples".to_string())
.text_content(
"Let's explore some real-world examples of using this trait:".to_string(),
ContentFormat::Markdown
)
.section_duration(10)
.build()
}
fn generate_trait_implementation_example(&self, trait_info: &TraitInfo) -> String {
format!(
"// Example implementation of {}\n\
struct MyStruct {{\n \
// Your fields here\n\
}}\n\n\
impl {} for MyStruct {{\n \
// Implement required methods\n\
}}",
trait_info.name, trait_info.name
)
}
fn generate_trait_template(&self, trait_info: &TraitInfo) -> String {
format!(
"// TODO: Implement {} for your custom type\n\
struct YourType {{\n \
// Add your fields\n\
}}\n\n\
impl {} for YourType {{\n \
// Implement the required methods\n\
}}",
trait_info.name, trait_info.name
)
}
pub fn generate_tutorial_from_type(&self, type_info: &TypeInfo) -> Tutorial {
TutorialBuilder::new(
format!("type_{}", type_info.name),
format!("Working with {} Type", type_info.name),
)
.description(format!(
"Learn how to use the {} type effectively",
type_info.name
))
.difficulty(DifficultyLevel::Beginner)
.duration(20)
.learning_objective(format!("Understand the {} type", type_info.name))
.learning_objective("Create and manipulate instances".to_string())
.category(TutorialCategory::CoreConcepts)
.tag("types".to_string())
.tag("rust".to_string())
.section("overview".to_string(), "Type Overview".to_string())
.text_content(
format!(
"{}\n\n**Type signature**: `{:?}`",
type_info.description, type_info.kind
),
ContentFormat::Markdown,
)
.section_duration(10)
.section("usage".to_string(), "Usage Examples".to_string())
.code_content(
self.generate_type_usage_example(type_info),
"rust".to_string(),
true,
)
.section_duration(10)
.build()
}
fn generate_type_usage_example(&self, type_info: &TypeInfo) -> String {
format!(
"// Example usage of {}\n\
let instance = {}::new();\n\
// Use the instance...",
type_info.name, type_info.name
)
}
pub fn get_recommended_tutorials(&self, user_id: &str) -> Vec<&Tutorial> {
if let Some(progress) = self.progress_tracker.user_progress.get(user_id) {
self.tutorials
.iter()
.filter(|tutorial| {
!progress.completed_tutorials.contains(&tutorial.id)
&& self.meets_prerequisites(tutorial, progress)
})
.collect()
} else {
self.tutorials
.iter()
.filter(|tutorial| tutorial.prerequisites.is_empty())
.collect()
}
}
fn meets_prerequisites(&self, tutorial: &Tutorial, progress: &UserProgress) -> bool {
tutorial
.prerequisites
.iter()
.all(|prereq| progress.completed_tutorials.contains(prereq))
}
pub fn start_tutorial(&mut self, user_id: String, tutorial_id: String) -> Result<(), String> {
if !self.tutorials.iter().any(|t| t.id == tutorial_id) {
return Err("Tutorial not found".to_string());
}
let user_id_clone = user_id.clone();
let progress = self
.progress_tracker
.user_progress
.entry(user_id)
.or_insert_with(|| UserProgress {
user_id: user_id_clone,
completed_tutorials: HashSet::new(),
current_tutorial: None,
current_section: None,
completion_percentage: 0.0,
time_spent: 0,
assessment_scores: HashMap::new(),
achievements: Vec::new(),
learning_preferences: LearningPreferences {
preferred_difficulty: DifficultyLevel::Beginner,
learning_pace: LearningPace::Normal,
content_types: vec![ContentType::Text, ContentType::Code],
reminder_frequency: ReminderFrequency::Weekly,
},
});
progress.current_tutorial = Some(tutorial_id);
progress.current_section = None;
progress.completion_percentage = 0.0;
Ok(())
}
pub fn complete_section(
&mut self,
user_id: &str,
tutorial_id: &str,
_section_id: &str,
) -> Result<(), String> {
let progress = self
.progress_tracker
.user_progress
.get_mut(user_id)
.ok_or("User not found")?;
if progress.current_tutorial.as_ref() != Some(&tutorial_id.to_string()) {
return Err("Tutorial not in progress".to_string());
}
let tutorial_info = self
.tutorials
.iter()
.find(|t| t.id == tutorial_id)
.map(|t| (t.sections.len(), t.title.clone(), t.difficulty.clone()));
if let Some((total_sections, title, difficulty)) = tutorial_info {
let completed_sections = progress.completed_tutorials.len();
progress.completion_percentage =
(completed_sections as f64 / total_sections as f64) * 100.0;
if progress.completion_percentage >= 100.0 {
progress.completed_tutorials.insert(tutorial_id.to_string());
progress.current_tutorial = None;
progress.current_section = None;
self.award_completion_achievement_info(user_id, tutorial_id, &title, &difficulty);
}
}
Ok(())
}
fn award_completion_achievement_info(
&mut self,
user_id: &str,
tutorial_id: &str,
title: &str,
difficulty: &DifficultyLevel,
) {
if let Some(progress) = self.progress_tracker.user_progress.get_mut(user_id) {
let achievement = Achievement {
id: format!("completed_{}", tutorial_id),
title: format!("Completed: {}", title),
description: format!("Successfully completed the {} tutorial", title),
icon: "🎓".to_string(),
earned_at: chrono::Utc::now(),
rarity: match difficulty {
DifficultyLevel::Beginner => AchievementRarity::Common,
DifficultyLevel::Intermediate => AchievementRarity::Uncommon,
DifficultyLevel::Advanced => AchievementRarity::Rare,
DifficultyLevel::Expert => AchievementRarity::Epic,
},
};
progress.achievements.push(achievement);
}
}
pub fn get_user_progress(&self, user_id: &str) -> Option<&UserProgress> {
self.progress_tracker.user_progress.get(user_id)
}
pub fn update_global_statistics(&mut self) {
self.progress_tracker.global_statistics.total_users =
self.progress_tracker.user_progress.len();
for tutorial in &self.tutorials {
let completion_count = self
.progress_tracker
.user_progress
.values()
.filter(|progress| progress.completed_tutorials.contains(&tutorial.id))
.count();
let completion_rate = if self.progress_tracker.global_statistics.total_users > 0 {
completion_count as f64 / self.progress_tracker.global_statistics.total_users as f64
} else {
0.0
};
self.progress_tracker
.global_statistics
.tutorial_completion_rates
.insert(tutorial.id.clone(), completion_rate);
}
}
}
#[allow(non_snake_case)]
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tutorial_builder() {
let tutorial = TutorialBuilder::new("test".to_string(), "Test Tutorial".to_string())
.description("Test description".to_string())
.difficulty(DifficultyLevel::Intermediate)
.duration(60)
.author("Test Author".to_string())
.build();
assert_eq!(tutorial.id, "test");
assert_eq!(tutorial.title, "Test Tutorial");
assert_eq!(tutorial.description, "Test description");
assert_eq!(tutorial.duration_minutes, 60);
assert!(matches!(tutorial.difficulty, DifficultyLevel::Intermediate));
}
#[test]
fn test_tutorial_system_creation() {
let config = TutorialConfig::default();
let system = TutorialSystem::new(config);
assert_eq!(system.tutorials.len(), 0);
assert_eq!(system.learning_paths.len(), 0);
}
#[test]
fn test_user_progress_tracking() {
let mut system = TutorialSystem::new(TutorialConfig::default());
let tutorial = TutorialBuilder::new("test".to_string(), "Test".to_string()).build();
system.add_tutorial(tutorial);
let result = system.start_tutorial("user1".to_string(), "test".to_string());
assert!(result.is_ok());
let progress = system.get_user_progress("user1");
assert!(progress.is_some());
assert_eq!(
progress.expect("expected valid value").current_tutorial,
Some("test".to_string())
);
}
#[test]
fn test_section_completion() {
let mut system = TutorialSystem::new(TutorialConfig::default());
let tutorial = TutorialBuilder::new("test".to_string(), "Test".to_string())
.section("section1".to_string(), "Section 1".to_string())
.build();
system.add_tutorial(tutorial);
system
.start_tutorial("user1".to_string(), "test".to_string())
.expect("expected valid value");
let result = system.complete_section("user1", "test", "section1");
assert!(result.is_ok());
}
#[test]
fn test_tutorial_generation_from_trait() {
let system = TutorialSystem::new(TutorialConfig::default());
let trait_info = TraitInfo {
name: "Display".to_string(),
path: "std::fmt::Display".to_string(),
description: "Trait for formatting output".to_string(),
methods: vec![],
associated_types: vec![],
generics: vec![],
supertraits: vec![],
implementations: vec![],
};
let tutorial = system.generate_tutorial_from_trait(&trait_info);
assert_eq!(tutorial.id, "trait_Display");
assert!(tutorial.title.contains("Display"));
assert!(!tutorial.sections.is_empty());
}
}