use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LearningResource {
pub id: String,
pub title: String,
pub description: String,
pub resource_type: ResourceType,
pub url: String,
pub provider: String,
pub difficulty: String,
pub duration_hours: Option<f32>,
pub cost: f32,
pub rating: Option<f32>,
pub skills: Vec<String>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum ResourceType {
Course,
Book,
Tutorial,
Documentation,
Video,
Project,
Practice,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LearningPath {
pub id: String,
pub target_skill: String,
pub target_level: String,
pub resources: Vec<LearningResource>,
pub estimated_total_hours: f32,
pub prerequisites: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LearningRecommendation {
pub skill_name: String,
pub current_level: String,
pub target_level: String,
pub priority: u8,
pub reason: String,
pub path: Option<LearningPath>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LearningPlan {
pub recommendations: Vec<LearningRecommendation>,
pub total_estimated_hours: f32,
pub focus_areas: Vec<String>,
}
pub struct LearningPathGenerator;
impl LearningPathGenerator {
pub fn generate_plan(
skill_gaps: &[super::skills::SkillGap],
_current_skills: &HashMap<String, super::skills::Skill>,
) -> LearningPlan {
let mut recommendations = Vec::new();
let mut total_hours = 0.0;
let mut focus_areas = Vec::new();
for gap in skill_gaps {
let priority = if gap.is_critical { 1 } else { 3 };
let reason = if gap.gap_score > 30 {
format!("Significant gap in {} - recommended for career growth", gap.skill_name)
} else {
format!("Strengthen {} skills to meet requirements", gap.skill_name)
};
let path = Self::generate_path(&gap.skill_name, gap.current_level, gap.required_level);
if let Some(ref p) = path {
total_hours += p.estimated_total_hours;
focus_areas.push(gap.skill_name.clone());
}
recommendations.push(LearningRecommendation {
skill_name: gap.skill_name.clone(),
current_level: format!("{:?}", gap.current_level),
target_level: format!("{:?}", gap.required_level),
priority,
reason,
path,
});
}
recommendations.sort_by_key(|r| r.priority);
LearningPlan {
recommendations,
total_estimated_hours: total_hours,
focus_areas,
}
}
fn generate_path(
skill: &str,
current: super::skills::SkillLevel,
target: super::skills::SkillLevel,
) -> Option<LearningPath> {
let resources = Self::get_resources_for_skill(skill, current, target)?;
let total_hours: f32 = resources.iter()
.filter_map(|r| r.duration_hours)
.sum();
Some(LearningPath {
id: uuid::Uuid::new_v4().to_string(),
target_skill: skill.to_string(),
target_level: format!("{:?}", target),
resources,
estimated_total_hours: total_hours,
prerequisites: Self::get_prerequisites(skill),
})
}
fn get_resources_for_skill(
skill: &str,
current: super::skills::SkillLevel,
target: super::skills::SkillLevel,
) -> Option<Vec<LearningResource>> {
let skill_lower = skill.to_lowercase();
let resources = match skill_lower.as_str() {
"rust" => Self::rust_resources(current, target),
"python" => Self::python_resources(current, target),
"javascript" | "js" => Self::javascript_resources(current, target),
"typescript" | "ts" => Self::typescript_resources(current, target),
"react" => Self::react_resources(current, target),
"docker" => Self::docker_resources(current, target),
"kubernetes" | "k8s" => Self::kubernetes_resources(current, target),
"aws" => Self::aws_resources(current, target),
"sql" => Self::sql_resources(current, target),
"go" | "golang" => Self::go_resources(current, target),
_ => return None,
};
if resources.is_empty() {
None
} else {
Some(resources)
}
}
fn rust_resources(current: super::skills::SkillLevel, target: super::skills::SkillLevel) -> Vec<LearningResource> {
let mut resources = Vec::new();
if current < super::skills::SkillLevel::Intermediate {
resources.push(LearningResource {
id: "rust-book".to_string(),
title: "The Rust Programming Language".to_string(),
description: "Official book covering Rust fundamentals".to_string(),
resource_type: ResourceType::Book,
url: "https://doc.rust-lang.org/book/".to_string(),
provider: "Rust Team".to_string(),
difficulty: "Beginner".to_string(),
duration_hours: Some(30.0),
cost: 0.0,
rating: Some(4.9),
skills: vec!["Rust".to_string()],
});
}
if target >= super::skills::SkillLevel::Advanced {
resources.push(LearningResource {
id: "rust-rxo".to_string(),
title: "Rust for Rustaceans".to_string(),
description: "Advanced Rust programming patterns".to_string(),
resource_type: ResourceType::Book,
url: "https://nostarch.com/rust-rustaceans".to_string(),
provider: "No Starch Press".to_string(),
difficulty: "Advanced".to_string(),
duration_hours: Some(20.0),
cost: 44.99,
rating: Some(4.8),
skills: vec!["Rust".to_string()],
});
resources.push(LearningResource {
id: "rust-practice".to_string(),
title: "Rustlings".to_string(),
description: "Small exercises to learn Rust".to_string(),
resource_type: ResourceType::Practice,
url: "https://github.com/rust-lang/rustlings".to_string(),
provider: "Rust Team".to_string(),
difficulty: "Intermediate".to_string(),
duration_hours: Some(15.0),
cost: 0.0,
rating: Some(4.7),
skills: vec!["Rust".to_string()],
});
}
resources
}
fn python_resources(current: super::skills::SkillLevel, target: super::skills::SkillLevel) -> Vec<LearningResource> {
let mut resources = Vec::new();
if current < super::skills::SkillLevel::Intermediate {
resources.push(LearningResource {
id: "py-official".to_string(),
title: "Python Official Tutorial".to_string(),
description: "Official Python documentation tutorial".to_string(),
resource_type: ResourceType::Tutorial,
url: "https://docs.python.org/3/tutorial/".to_string(),
provider: "Python.org".to_string(),
difficulty: "Beginner".to_string(),
duration_hours: Some(20.0),
cost: 0.0,
rating: Some(4.8),
skills: vec!["Python".to_string()],
});
}
if target >= super::skills::SkillLevel::Advanced {
resources.push(LearningResource {
id: "py-fluent".to_string(),
title: "Fluent Python".to_string(),
description: "Deep dive into Python idioms".to_string(),
resource_type: ResourceType::Book,
url: "https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/".to_string(),
provider: "O'Reilly".to_string(),
difficulty: "Advanced".to_string(),
duration_hours: Some(25.0),
cost: 49.99,
rating: Some(4.9),
skills: vec!["Python".to_string()],
});
}
resources
}
fn javascript_resources(_current: super::skills::SkillLevel, _target: super::skills::SkillLevel) -> Vec<LearningResource> {
vec![LearningResource {
id: "js-mdn".to_string(),
title: "MDN JavaScript Guide".to_string(),
description: "Comprehensive JavaScript documentation".to_string(),
resource_type: ResourceType::Documentation,
url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide".to_string(),
provider: "Mozilla".to_string(),
difficulty: "Beginner".to_string(),
duration_hours: Some(25.0),
cost: 0.0,
rating: Some(4.9),
skills: vec!["JavaScript".to_string()],
}]
}
fn typescript_resources(_current: super::skills::SkillLevel, _target: super::skills::SkillLevel) -> Vec<LearningResource> {
vec![LearningResource {
id: "ts-handbook".to_string(),
title: "TypeScript Handbook".to_string(),
description: "Official TypeScript documentation".to_string(),
resource_type: ResourceType::Documentation,
url: "https://www.typescriptlang.org/docs/".to_string(),
provider: "Microsoft".to_string(),
difficulty: "Intermediate".to_string(),
duration_hours: Some(15.0),
cost: 0.0,
rating: Some(4.8),
skills: vec!["TypeScript".to_string()],
}]
}
fn react_resources(_current: super::skills::SkillLevel, _target: super::skills::SkillLevel) -> Vec<LearningResource> {
vec![LearningResource {
id: "react-docs".to_string(),
title: "React Documentation".to_string(),
description: "Official React tutorial and docs".to_string(),
resource_type: ResourceType::Documentation,
url: "https://react.dev/learn".to_string(),
provider: "Meta".to_string(),
difficulty: "Intermediate".to_string(),
duration_hours: Some(20.0),
cost: 0.0,
rating: Some(4.9),
skills: vec!["React".to_string()],
}]
}
fn docker_resources(_current: super::skills::SkillLevel, _target: super::skills::SkillLevel) -> Vec<LearningResource> {
vec![LearningResource {
id: "docker-get-started".to_string(),
title: "Docker Get Started".to_string(),
description: "Official Docker tutorial".to_string(),
resource_type: ResourceType::Tutorial,
url: "https://docs.docker.com/get-started/".to_string(),
provider: "Docker".to_string(),
difficulty: "Beginner".to_string(),
duration_hours: Some(10.0),
cost: 0.0,
rating: Some(4.7),
skills: vec!["Docker".to_string()],
}]
}
fn kubernetes_resources(_current: super::skills::SkillLevel, _target: super::skills::SkillLevel) -> Vec<LearningResource> {
vec![LearningResource {
id: "k8s-learn".to_string(),
title: "Kubernetes Learning".to_string(),
description: "Official Kubernetes tutorials".to_string(),
resource_type: ResourceType::Tutorial,
url: "https://kubernetes.io/docs/tutorials/".to_string(),
provider: "CNCF".to_string(),
difficulty: "Intermediate".to_string(),
duration_hours: Some(20.0),
cost: 0.0,
rating: Some(4.8),
skills: vec!["Kubernetes".to_string()],
}]
}
fn aws_resources(_current: super::skills::SkillLevel, _target: super::skills::SkillLevel) -> Vec<LearningResource> {
vec![LearningResource {
id: "aws-cloud-practitioner".to_string(),
title: "AWS Cloud Practitioner".to_string(),
description: "AWS fundamentals certification".to_string(),
resource_type: ResourceType::Course,
url: "https://aws.amazon.com/training/path-cloud-practitioner/".to_string(),
provider: "AWS".to_string(),
difficulty: "Beginner".to_string(),
duration_hours: Some(15.0),
cost: 0.0,
rating: Some(4.7),
skills: vec!["AWS".to_string()],
}]
}
fn sql_resources(_current: super::skills::SkillLevel, _target: super::skills::SkillLevel) -> Vec<LearningResource> {
vec![LearningResource {
id: "sql-z".to_string(),
title: "SQLZoo".to_string(),
description: "Interactive SQL tutorials".to_string(),
resource_type: ResourceType::Tutorial,
url: "https://sqlzoo.net/".to_string(),
provider: "SQLZoo".to_string(),
difficulty: "Beginner".to_string(),
duration_hours: Some(10.0),
cost: 0.0,
rating: Some(4.6),
skills: vec!["SQL".to_string()],
}]
}
fn go_resources(_current: super::skills::SkillLevel, _target: super::skills::SkillLevel) -> Vec<LearningResource> {
vec![LearningResource {
id: "go-tour".to_string(),
title: "A Tour of Go".to_string(),
description: "Interactive Go tutorial".to_string(),
resource_type: ResourceType::Tutorial,
url: "https://go.dev/tour/welcome/1".to_string(),
provider: "Google".to_string(),
difficulty: "Beginner".to_string(),
duration_hours: Some(5.0),
cost: 0.0,
rating: Some(4.9),
skills: vec!["Go".to_string()],
}]
}
fn get_prerequisites(skill: &str) -> Vec<String> {
match skill.to_lowercase().as_str() {
"react" => vec!["JavaScript".to_string(), "TypeScript".to_string()],
"kubernetes" => vec!["Docker".to_string(), "Linux".to_string()],
"typescript" => vec!["JavaScript".to_string()],
_ => Vec::new(),
}
}
}