use anyhow::{Result, anyhow}; use std::{ fs, path::{Path, PathBuf}, }; pub fn get_exercise_name(exercise_path: &Path) -> String { exercise_path .file_name() .unwrap_or_default() .to_string_lossy() .to_string() } pub fn get_exercise_language(exercise_path: &Path) -> Result { // Extract the language from path (data/python/exercises/... => python) let parts: Vec<_> = exercise_path.components().collect(); for (i, part) in parts.iter().enumerate() { if i > 0 && part.as_os_str() == "eval_code" { if i + 1 < parts.len() { let language = parts[i + 1].as_os_str().to_string_lossy().to_string(); return Ok(language); } } } Err(anyhow!( "Could not determine language from path: {:?}", exercise_path )) } pub fn find_exercises( framework_path: &Path, languages: &[&str], max_per_language: Option, ) -> Result> { let mut all_exercises = Vec::new(); println!("Searching for exercises in languages: {:?}", languages); for language in languages { let language_dir = framework_path .join("eval_code") .join(language) .join("exercises") .join("practice"); println!("Checking language directory: {:?}", language_dir); if !language_dir.exists() { println!("Warning: Language directory not found: {:?}", language_dir); continue; } let mut exercises = Vec::new(); match fs::read_dir(&language_dir) { Ok(entries) => { for entry_result in entries { match entry_result { Ok(entry) => { let path = entry.path(); if path.is_dir() { // Special handling for "internal" directory if *language == "internal" { // Check for repo_info.json to validate it's an internal exercise let repo_info_path = path.join(".meta").join("repo_info.json"); let instructions_path = path.join(".docs").join("instructions.md"); if repo_info_path.exists() && instructions_path.exists() { exercises.push(path); } } else { // Map the language to the file extension - original code let language_extension = match *language { "python" => "py", "go" => "go", "rust" => "rs", "typescript" => "ts", "javascript" => "js", "ruby" => "rb", "php" => "php", "bash" => "sh", "multi" => "diff", _ => continue, // Skip unsupported languages }; // Check if this is a valid exercise with instructions and example let instructions_path = path.join(".docs").join("instructions.md"); let has_instructions = instructions_path.exists(); let example_path = path .join(".meta") .join(format!("example.{}", language_extension)); let has_example = example_path.exists(); if has_instructions && has_example { exercises.push(path); } } } } Err(err) => println!("Error reading directory entry: {}", err), } } } Err(err) => println!( "Error reading directory {}: {}", language_dir.display(), err ), } // Sort exercises by name for consistent selection exercises.sort_by(|a, b| { let a_name = a.file_name().unwrap_or_default().to_string_lossy(); let b_name = b.file_name().unwrap_or_default().to_string_lossy(); a_name.cmp(&b_name) }); // Apply the limit if specified if let Some(limit) = max_per_language { if exercises.len() > limit { println!( "Limiting {} exercises to {} for language {}", exercises.len(), limit, language ); exercises.truncate(limit); } } println!( "Found {} exercises for language {}: {:?}", exercises.len(), language, exercises .iter() .map(|p| p.file_name().unwrap_or_default().to_string_lossy()) .collect::>() ); all_exercises.extend(exercises); } Ok(all_exercises) }