1use anyhow::{Result, anyhow};
2use std::{
3 fs,
4 path::{Path, PathBuf},
5};
6
7pub fn get_exercise_name(exercise_path: &Path) -> String {
8 exercise_path
9 .file_name()
10 .unwrap_or_default()
11 .to_string_lossy()
12 .to_string()
13}
14
15pub fn get_exercise_language(exercise_path: &Path) -> Result<String> {
16 // Extract the language from path (data/python/exercises/... => python)
17 let parts: Vec<_> = exercise_path.components().collect();
18
19 for (i, part) in parts.iter().enumerate() {
20 if i > 0 && part.as_os_str() == "eval_code" {
21 if i + 1 < parts.len() {
22 let language = parts[i + 1].as_os_str().to_string_lossy().to_string();
23 return Ok(language);
24 }
25 }
26 }
27
28 Err(anyhow!(
29 "Could not determine language from path: {:?}",
30 exercise_path
31 ))
32}
33
34pub fn find_exercises(
35 framework_path: &Path,
36 languages: &[&str],
37 max_per_language: Option<usize>,
38) -> Result<Vec<PathBuf>> {
39 let mut all_exercises = Vec::new();
40
41 println!("Searching for exercises in languages: {:?}", languages);
42
43 for language in languages {
44 let language_dir = framework_path
45 .join("eval_code")
46 .join(language)
47 .join("exercises")
48 .join("practice");
49
50 println!("Checking language directory: {:?}", language_dir);
51 if !language_dir.exists() {
52 println!("Warning: Language directory not found: {:?}", language_dir);
53 continue;
54 }
55
56 let mut exercises = Vec::new();
57 match fs::read_dir(&language_dir) {
58 Ok(entries) => {
59 for entry_result in entries {
60 match entry_result {
61 Ok(entry) => {
62 let path = entry.path();
63
64 if path.is_dir() {
65 // Special handling for "internal" directory
66 if *language == "internal" {
67 // Check for repo_info.json to validate it's an internal exercise
68 let repo_info_path = path.join(".meta").join("repo_info.json");
69 let instructions_path =
70 path.join(".docs").join("instructions.md");
71
72 if repo_info_path.exists() && instructions_path.exists() {
73 exercises.push(path);
74 }
75 } else {
76 // Map the language to the file extension - original code
77 let language_extension = match *language {
78 "python" => "py",
79 "go" => "go",
80 "rust" => "rs",
81 "typescript" => "ts",
82 "javascript" => "js",
83 "ruby" => "rb",
84 "php" => "php",
85 "bash" => "sh",
86 "multi" => "diff",
87 _ => continue, // Skip unsupported languages
88 };
89
90 // Check if this is a valid exercise with instructions and example
91 let instructions_path =
92 path.join(".docs").join("instructions.md");
93 let has_instructions = instructions_path.exists();
94 let example_path = path
95 .join(".meta")
96 .join(format!("example.{}", language_extension));
97 let has_example = example_path.exists();
98
99 if has_instructions && has_example {
100 exercises.push(path);
101 }
102 }
103 }
104 }
105 Err(err) => println!("Error reading directory entry: {}", err),
106 }
107 }
108 }
109 Err(err) => println!(
110 "Error reading directory {}: {}",
111 language_dir.display(),
112 err
113 ),
114 }
115
116 // Sort exercises by name for consistent selection
117 exercises.sort_by(|a, b| {
118 let a_name = a.file_name().unwrap_or_default().to_string_lossy();
119 let b_name = b.file_name().unwrap_or_default().to_string_lossy();
120 a_name.cmp(&b_name)
121 });
122
123 // Apply the limit if specified
124 if let Some(limit) = max_per_language {
125 if exercises.len() > limit {
126 println!(
127 "Limiting {} exercises to {} for language {}",
128 exercises.len(),
129 limit,
130 language
131 );
132 exercises.truncate(limit);
133 }
134 }
135
136 println!(
137 "Found {} exercises for language {}: {:?}",
138 exercises.len(),
139 language,
140 exercises
141 .iter()
142 .map(|p| p.file_name().unwrap_or_default().to_string_lossy())
143 .collect::<Vec<_>>()
144 );
145 all_exercises.extend(exercises);
146 }
147
148 Ok(all_exercises)
149}