From 51d3be523296227797cb9954c8e6d4a35eb6e450 Mon Sep 17 00:00:00 2001 From: Ray Okamoto Date: Sun, 13 Jul 2025 14:00:18 +0930 Subject: [PATCH 1/4] refactor(problem): use `RunCommand` for solve and test This also fixes a bug where custom script files would not be used instead of the default script file. --- crates/cli/src/problem/solve.rs | 113 ++++++++----------------------- crates/cli/src/problem/test.rs | 115 ++++++++------------------------ 2 files changed, 54 insertions(+), 174 deletions(-) diff --git a/crates/cli/src/problem/solve.rs b/crates/cli/src/problem/solve.rs index 0f2d002..891edd1 100644 --- a/crates/cli/src/problem/solve.rs +++ b/crates/cli/src/problem/solve.rs @@ -1,12 +1,11 @@ -use std::ffi::OsStr; -use std::fs::{self, File}; +use std::fs::File; +use std::io::Write; use std::path::Path; -use anyhow::{bail, Context, Result}; -use normpath::PathExt; -use subprocess::Exec; +use anyhow::{Context, Result}; use super::sync_mappings::get_problem; +use crate::problem::run::{RunCommand, RunnableCategory, RunnableFile}; use crate::util::get_project_root; use crate::{config::Settings, util::get_input_files_in_directory}; @@ -19,109 +18,51 @@ pub fn solve( solution_lang: Option<&String>, ) -> Result<()> { let project_root = get_project_root()?; - let problem = project_root.join(get_problem(problems_dir, problem_name)?); + let problem_path = project_root.join(get_problem(problems_dir, problem_name)?); let solution_lang = solution_lang.unwrap_or(&settings.problem.default_lang); - let mut solution_file = problem.join(format!("solutions/solution.{solution_lang}")); - if solution_file_name.is_some() { - solution_file = problem.join(format!( - "solutions/{}", - solution_file_name.context("Failed to get solution file name")? - )); - } - solution_file = solution_file.normalize()?.into(); + let mut solution_file = format!("solution.{solution_lang}"); - if !fs::exists(&solution_file).expect("Failed to check if path exists") { - bail!("Solution file does not exist: {:?}", solution_file); + // Use custom solution file or script file if it exists + if solution_file_name.is_some() { + solution_file = solution_file_name + .context("Failed to get solution file name")? + .to_string(); } - eprintln!("Using solution file at: {}", solution_file.display()); - - let bin_file = problem.join("solutions/solution.out"); - let script_file = problem.join(format!("solutions/solution.{solution_lang}")); - - let lang_settings = settings - .problem - .solution - .get(solution_lang) - .context(format!( - "Could not get settings for language `{solution_lang}`" - ))?; - - let compile_command = lang_settings.compile_command.clone(); + let runnable_file = + RunnableFile::new(settings, RunnableCategory::Solution, Some(&solution_file))?; - // Check if the solution file is a script (if it needs compilation or not) - let needs_compilation = compile_command.is_some(); - let compile_command = compile_command.unwrap_or_default(); - if needs_compilation && compile_command.is_empty() { - bail!("compile_command specified in the settings, but array is empty"); - } - - if needs_compilation { - let mut cmd_iter = compile_command.iter(); - let mut final_cmd = Exec::cmd(cmd_iter.next().context("Failed to get command")?); - for c in cmd_iter { - // Replace strings where necessary - final_cmd = match c.as_str() { - "@in_file" => final_cmd.arg(&solution_file), - "@bin_file" => final_cmd.arg(&bin_file), - _ => final_cmd.arg(c), - } - } - eprint!("Compiling the solution file... "); - // Run the compile command - final_cmd.join()?; - eprintln!("Done"); - } + let run_command = RunCommand::new( + settings, + &problem_path, + &runnable_file, + problem_path.join("solutions/solution.out"), + problem_path.join(&solution_file), + )?; - let run_command = lang_settings.run_command.clone().unwrap_or_default(); - if run_command.is_empty() { - bail!("No run command specified in the settings. It must be specified!"); - } - let cmd_iter = run_command.iter(); - let test_files = get_input_files_in_directory(problem.join("tests"))?; + let test_files = get_input_files_in_directory(problem_path.join("tests"))?; eprintln!("Running the solution file for each test case..."); // Run the file for every test input and generate the corresponding output for test_file in test_files { - let input_file_path = problem.join(format!("tests/{test_file}")); - let output_file_path = problem.join(format!( + let input_file_path = problem_path.join(format!("tests/{test_file}")); + let output_file_path = problem_path.join(format!( "tests/{}.out", test_file .strip_suffix(".in") .context("Failed to strip suffix of test file")? )); - let mut cmd_iter_clone = cmd_iter.clone(); - let cmd = cmd_iter_clone.next().context("Failed to get command")?; - let mut final_cmd = Exec::cmd(match cmd.as_str() { - "@bin_file" => bin_file.as_os_str(), - "@script_file" => script_file.as_os_str(), - _ => OsStr::new(cmd), - }); + let result = run_command.get_result(Some(&input_file_path))?; + let mut output_file = File::create(output_file_path)?; + output_file.write_all(result.output.as_bytes())?; - for c in cmd_iter_clone { - // Replace strings where necessary - final_cmd = match c.as_str() { - "@bin_file" => final_cmd.arg(&bin_file), - "@script_file" => final_cmd.arg(&script_file), - _ => final_cmd.arg(c), - } - } - - let input_file = File::open(input_file_path)?; - let output_file = File::create(output_file_path)?; - - final_cmd = final_cmd.stdin(input_file).stdout(output_file); - final_cmd.capture()?; eprintln!(" - generated output for test file: {test_file}"); } eprintln!("Finished generating outputs for all test cases"); - // Delete the compiled run file, if it exists - if bin_file.exists() { - fs::remove_file(bin_file)?; - } + run_command.cleanup()?; Ok(()) } diff --git a/crates/cli/src/problem/test.rs b/crates/cli/src/problem/test.rs index 3933828..4578584 100644 --- a/crates/cli/src/problem/test.rs +++ b/crates/cli/src/problem/test.rs @@ -1,14 +1,12 @@ -use std::ffi::OsStr; -use std::fs::{self, File}; +use std::fs::File; use std::io::Read; use std::path::Path; -use std::time::{Duration, Instant}; +use std::time::Duration; -use anyhow::{bail, Context, Result}; -use normpath::PathExt; -use subprocess::{Exec, Redirection}; +use anyhow::{Context, Result}; use crate::config::Settings; +use crate::problem::run::{RunCommand, RunnableCategory, RunnableFile}; use crate::util::{get_input_files_in_directory, get_project_root}; use super::sync_mappings::get_problem; @@ -22,67 +20,30 @@ pub fn test( solution_lang: Option<&String>, ) -> Result<()> { let project_root = get_project_root()?; - let problem = project_root.join(get_problem(problems_dir, problem_name)?); + let problem_path = project_root.join(get_problem(problems_dir, problem_name)?); let solution_lang = solution_lang.unwrap_or(&settings.problem.default_lang); - let mut solution_file = problem.join(format!("solutions/solution.{solution_lang}")); - if solution_file_name.is_some() { - solution_file = problem.join(format!( - "solutions/{}", - solution_file_name.context("Failed to get solution file name")? - )); - } - solution_file = solution_file.normalize()?.into(); + let mut solution_file = format!("solution.{solution_lang}"); - if !fs::exists(&solution_file).expect("Failed to check if path exists") { - bail!("Solution file does not exist: {:?}", solution_file); + // Use custom solution file or script file if it exists + if solution_file_name.is_some() { + solution_file = solution_file_name + .context("Failed to get solution file name")? + .to_string(); } - eprintln!("Using solution file at: {}", solution_file.display()); + let runnable_file = + RunnableFile::new(settings, RunnableCategory::Solution, Some(&solution_file))?; - let bin_file = problem.join("solutions/solution.out"); - let script_file = problem.join(format!("solutions/solution.{solution_lang}")); + let run_command = RunCommand::new( + settings, + &problem_path, + &runnable_file, + problem_path.join("solutions/solution.out"), + problem_path.join(&solution_file), + )?; - let lang_settings = settings - .problem - .solution - .get(solution_lang) - .context(format!( - "Could not get settings for language `{solution_lang}`" - ))?; - - let compile_command = lang_settings.compile_command.clone(); - - // Check if the solution file is a script (if it needs compilation or not) - let needs_compilation = compile_command.is_some(); - let compile_command = compile_command.unwrap_or_default(); - if needs_compilation && compile_command.is_empty() { - bail!("compile_command specified in the settings, but array is empty"); - } - - if needs_compilation { - let mut cmd_iter = compile_command.iter(); - let mut final_cmd = Exec::cmd(cmd_iter.next().context("Failed to get command")?); - for c in cmd_iter { - // Replace strings where necessary - final_cmd = match c.as_str() { - "@in_file" => final_cmd.arg(&solution_file), - "@bin_file" => final_cmd.arg(&bin_file), - _ => final_cmd.arg(c), - } - } - eprint!("Compiling the solution file... "); - // Run the compile command - final_cmd.join()?; - eprintln!("Done"); - } - - let run_command = lang_settings.run_command.clone().unwrap_or_default(); - if run_command.is_empty() { - bail!("No run command specified in the settings. It must be specified!"); - } - let cmd_iter = run_command.iter(); - let test_files = get_input_files_in_directory(problem.join("tests"))?; + let test_files = get_input_files_in_directory(problem_path.join("tests"))?; eprintln!("Running the solution file for each test case..."); @@ -91,38 +52,19 @@ pub fn test( let mut total_time: Duration = Duration::new(0, 0); for test_file in test_files { - let input_file_path = problem.join(format!("tests/{test_file}")); - let output_file_path = problem.join(format!( + let input_file_path = problem_path.join(format!("tests/{test_file}")); + let output_file_path = problem_path.join(format!( "tests/{}.out", test_file .strip_suffix(".in") .context("Failed to strip suffix of test file")? )); - let mut cmd_iter_clone = cmd_iter.clone(); - let cmd = cmd_iter_clone.next().context("Failed to get command")?; - let mut final_cmd = Exec::cmd(match cmd.as_str() { - "@bin_file" => bin_file.as_os_str(), - "@script_file" => script_file.as_os_str(), - _ => OsStr::new(cmd), - }); - - for c in cmd_iter_clone { - // Replace strings where necessary - final_cmd = match c.as_str() { - "@bin_file" => final_cmd.arg(&bin_file), - "@script_file" => final_cmd.arg(&script_file), - _ => final_cmd.arg(c), - } - } + let result = run_command.get_result(Some(&input_file_path))?; - let input_file = File::open(input_file_path)?; let mut output_file = File::open(output_file_path)?; - - let start_time = Instant::now(); - final_cmd = final_cmd.stdin(input_file).stdout(Redirection::Pipe); - let out_str = final_cmd.capture()?.stdout_str(); - let elapsed_time = start_time.elapsed(); + let out_str = result.output; + let elapsed_time = result.elapsed_time; // Compare the output with the expected output let expected: &mut Vec = &mut Vec::new(); @@ -150,10 +92,7 @@ pub fn test( total_time.as_secs_f64() ); - // Delete the compiled run file, if it exists - if bin_file.exists() { - fs::remove_file(bin_file)?; - } + run_command.cleanup()?; Ok(()) } From 9a9f2809dde2aff6cbcb5f1337559e6e5b82b74c Mon Sep 17 00:00:00 2001 From: Ray Okamoto Date: Wed, 16 Jul 2025 01:37:39 +0930 Subject: [PATCH 2/4] feat: add lang option for `RunnableFile` --- crates/cli/src/cli/comp.rs | 11 ++++++-- crates/cli/src/cli/problem.rs | 24 +++++++++------- crates/cli/src/comp/solve.rs | 6 ++-- crates/cli/src/comp/test.rs | 6 ++-- crates/cli/src/problem/run.rs | 50 +++++++++++++++++++++++---------- crates/cli/src/problem/solve.rs | 22 +++------------ crates/cli/src/problem/test.rs | 22 +++------------ 7 files changed, 72 insertions(+), 69 deletions(-) diff --git a/crates/cli/src/cli/comp.rs b/crates/cli/src/cli/comp.rs index 46a30cd..4f0f399 100644 --- a/crates/cli/src/cli/comp.rs +++ b/crates/cli/src/cli/comp.rs @@ -5,6 +5,7 @@ use clap::{Arg, ArgAction, ArgMatches, Command}; use crate::comp::{add, create, finish, list, remove, rename, solve, test}; use crate::config::get_settings; +use crate::problem::run::{RunnableCategory, RunnableFile}; use crate::util::get_project_root; pub fn cli() -> Command { @@ -189,7 +190,10 @@ pub fn exec(args: &ArgMatches) -> Result<()> { .context("Competition name is required")?; let solution_lang = cmd.try_get_one::("lang")?; - solve::solve(&settings, &problems_dir, comp_name, solution_lang)?; + let solution_file = + RunnableFile::new(&settings, RunnableCategory::Solution, None, solution_lang)?; + + solve::solve(&settings, &problems_dir, comp_name, solution_file)?; } Some(("test", cmd)) => { let comp_name = cmd @@ -197,7 +201,10 @@ pub fn exec(args: &ArgMatches) -> Result<()> { .context("Competition name is required")?; let solution_lang = cmd.try_get_one::("lang")?; - test::test(&settings, &problems_dir, comp_name, solution_lang)?; + let solution_file = + RunnableFile::new(&settings, RunnableCategory::Solution, None, solution_lang)?; + + test::test(&settings, &problems_dir, comp_name, solution_file)?; } _ => {} } diff --git a/crates/cli/src/cli/problem.rs b/crates/cli/src/cli/problem.rs index a664f9d..fa5e86a 100644 --- a/crates/cli/src/cli/problem.rs +++ b/crates/cli/src/cli/problem.rs @@ -190,7 +190,7 @@ pub fn exec(args: &ArgMatches) -> Result<()> { let mut solution_files: Vec = Vec::new(); for f in files { let solution_file = - RunnableFile::new(&settings, RunnableCategory::Solution, Some(f)); + RunnableFile::new(&settings, RunnableCategory::Solution, Some(f), None); solution_files.push(solution_file?); } @@ -231,7 +231,7 @@ pub fn exec(args: &ArgMatches) -> Result<()> { let mut solution_files: Vec = Vec::new(); for f in files { let solution_file = - RunnableFile::new(&settings, RunnableCategory::Solution, Some(f)); + RunnableFile::new(&settings, RunnableCategory::Solution, Some(f), None); solution_files.push(solution_file?); } @@ -239,6 +239,7 @@ pub fn exec(args: &ArgMatches) -> Result<()> { &settings, RunnableCategory::Generator, cmd.try_get_one::("generator-file")?, + None, )?; let fuzz_args = fuzz::FuzzArgs { @@ -260,6 +261,7 @@ pub fn exec(args: &ArgMatches) -> Result<()> { &settings, RunnableCategory::Generator, cmd.try_get_one::("file")?, + None, )?; let test_name = cmd @@ -281,16 +283,17 @@ pub fn exec(args: &ArgMatches) -> Result<()> { None => &get_problem_from_cwd(&problems_dir)?, }; - let solution_file = cmd.try_get_one::("file")?.map(|f| f.as_str()); + let solution_file = cmd.try_get_one::("file")?; let solution_lang = cmd.try_get_one::("lang")?; - solve::solve( + let solution_file = RunnableFile::new( &settings, - &problems_dir, - problem_name, + RunnableCategory::Solution, solution_file, solution_lang, )?; + + solve::solve(&settings, &problems_dir, problem_name, &solution_file)?; } Some(("test", cmd)) => { let problem_name = match cmd.try_get_one::("problem")? { @@ -298,16 +301,17 @@ pub fn exec(args: &ArgMatches) -> Result<()> { None => &get_problem_from_cwd(&problems_dir)?, }; - let solution_file = cmd.try_get_one::("file")?.map(|f| f.as_str()); + let solution_file = cmd.try_get_one::("file")?; let solution_lang = cmd.try_get_one::("lang")?; - test::test( + let solution_file = RunnableFile::new( &settings, - &problems_dir, - problem_name, + RunnableCategory::Solution, solution_file, solution_lang, )?; + + test::test(&settings, &problems_dir, problem_name, &solution_file)?; } _ => {} } diff --git a/crates/cli/src/comp/solve.rs b/crates/cli/src/comp/solve.rs index bb6e2b4..af355f5 100644 --- a/crates/cli/src/comp/solve.rs +++ b/crates/cli/src/comp/solve.rs @@ -5,6 +5,7 @@ use anyhow::{bail, Context, Result}; use serde_json::from_reader; use crate::config::Settings; +use crate::problem::run::RunnableFile; use crate::problem::solve::solve as problem_solve; use super::{Competitions, COMPETITIONS_FILE}; @@ -13,7 +14,7 @@ pub fn solve( settings: &Settings, problems_dir: &Path, comp_name: &str, - solution_lang: Option<&String>, + solution_file: RunnableFile, ) -> Result<()> { let comp_file_path = problems_dir.join(COMPETITIONS_FILE); if !fs::exists(&comp_file_path)? { @@ -34,8 +35,7 @@ pub fn solve( settings, problems_dir, problem_name.as_str(), - None, - solution_lang, + &solution_file, )?; } diff --git a/crates/cli/src/comp/test.rs b/crates/cli/src/comp/test.rs index e5eb60a..4650714 100644 --- a/crates/cli/src/comp/test.rs +++ b/crates/cli/src/comp/test.rs @@ -5,6 +5,7 @@ use anyhow::{bail, Context, Result}; use serde_json::from_reader; use crate::config::Settings; +use crate::problem::run::RunnableFile; use crate::problem::test::test as problem_test; use super::{Competitions, COMPETITIONS_FILE}; @@ -13,7 +14,7 @@ pub fn test( settings: &Settings, problems_dir: &Path, comp_name: &str, - solution_lang: Option<&String>, + solution_file: RunnableFile, ) -> Result<()> { let comp_file_path = problems_dir.join(COMPETITIONS_FILE); if !fs::exists(&comp_file_path)? { @@ -34,8 +35,7 @@ pub fn test( settings, problems_dir, problem_name.as_str(), - None, - solution_lang, + &solution_file, )?; } diff --git a/crates/cli/src/problem/run.rs b/crates/cli/src/problem/run.rs index 09d6948..723eabe 100644 --- a/crates/cli/src/problem/run.rs +++ b/crates/cli/src/problem/run.rs @@ -38,26 +38,46 @@ pub struct RunnableFile { impl RunnableFile { /// Sets the file name if given, and infers the language from the file - /// extension. Otherwise defaults to the category name and the default - /// language from the settings. + /// extension. If the language is provided but not the file name, the + /// default language file is used for that category. + /// + /// If neither is provided, it defaults to the category name + /// and the default language from the settings. pub fn new( settings: &Settings, category: RunnableCategory, name: Option<&String>, + language: Option<&String>, ) -> Result { - let lang: String; - let filename: String; - if name.is_none() { - lang = match category { - RunnableCategory::Solution => settings.problem.default_lang.clone(), - RunnableCategory::Generator => settings.problem.default_generator_lang.clone(), - }; - filename = format!("{category}.{lang}"); - } else { - lang = - get_lang_from_extension(name.context("Failed to get filename of runnable file")?)?; - filename = name.unwrap().to_string(); - } + let (filename, lang) = match (name, language) { + (Some(name), Some(lang)) => { + let file_lang = get_lang_from_extension(name) + .context("Failed to get language from file extension")?; + if file_lang != *lang { + bail!( + "Language from file extension ({file_lang}) does not match provided language ({lang})" + ); + } + (name.to_string(), lang.to_string()) + } + (Some(name), None) => { + let lang = get_lang_from_extension(name) + .context("Failed to get language from file extension")?; + (name.to_string(), lang) + } + (None, Some(lang)) => { + let filename = format!("{category}.{lang}"); + (filename, lang.to_string()) + } + (None, None) => { + let lang = match category { + RunnableCategory::Solution => settings.problem.default_lang.clone(), + RunnableCategory::Generator => settings.problem.default_generator_lang.clone(), + }; + let filename = format!("{category}.{lang}"); + (filename, lang) + } + }; Ok(Self { name: filename, diff --git a/crates/cli/src/problem/solve.rs b/crates/cli/src/problem/solve.rs index 891edd1..d63a014 100644 --- a/crates/cli/src/problem/solve.rs +++ b/crates/cli/src/problem/solve.rs @@ -5,7 +5,7 @@ use std::path::Path; use anyhow::{Context, Result}; use super::sync_mappings::get_problem; -use crate::problem::run::{RunCommand, RunnableCategory, RunnableFile}; +use crate::problem::run::{RunCommand, RunnableFile}; use crate::util::get_project_root; use crate::{config::Settings, util::get_input_files_in_directory}; @@ -14,31 +14,17 @@ pub fn solve( settings: &Settings, problems_dir: &Path, problem_name: &str, - solution_file_name: Option<&str>, - solution_lang: Option<&String>, + solution_file: &RunnableFile, ) -> Result<()> { let project_root = get_project_root()?; let problem_path = project_root.join(get_problem(problems_dir, problem_name)?); - let solution_lang = solution_lang.unwrap_or(&settings.problem.default_lang); - let mut solution_file = format!("solution.{solution_lang}"); - - // Use custom solution file or script file if it exists - if solution_file_name.is_some() { - solution_file = solution_file_name - .context("Failed to get solution file name")? - .to_string(); - } - - let runnable_file = - RunnableFile::new(settings, RunnableCategory::Solution, Some(&solution_file))?; - let run_command = RunCommand::new( settings, &problem_path, - &runnable_file, + solution_file, problem_path.join("solutions/solution.out"), - problem_path.join(&solution_file), + problem_path.join(format!("{solution_file}")), )?; let test_files = get_input_files_in_directory(problem_path.join("tests"))?; diff --git a/crates/cli/src/problem/test.rs b/crates/cli/src/problem/test.rs index 4578584..da8af82 100644 --- a/crates/cli/src/problem/test.rs +++ b/crates/cli/src/problem/test.rs @@ -6,7 +6,7 @@ use std::time::Duration; use anyhow::{Context, Result}; use crate::config::Settings; -use crate::problem::run::{RunCommand, RunnableCategory, RunnableFile}; +use crate::problem::run::{RunCommand, RunnableFile}; use crate::util::{get_input_files_in_directory, get_project_root}; use super::sync_mappings::get_problem; @@ -16,31 +16,17 @@ pub fn test( settings: &Settings, problems_dir: &Path, problem_name: &str, - solution_file_name: Option<&str>, - solution_lang: Option<&String>, + solution_file: &RunnableFile, ) -> Result<()> { let project_root = get_project_root()?; let problem_path = project_root.join(get_problem(problems_dir, problem_name)?); - let solution_lang = solution_lang.unwrap_or(&settings.problem.default_lang); - let mut solution_file = format!("solution.{solution_lang}"); - - // Use custom solution file or script file if it exists - if solution_file_name.is_some() { - solution_file = solution_file_name - .context("Failed to get solution file name")? - .to_string(); - } - - let runnable_file = - RunnableFile::new(settings, RunnableCategory::Solution, Some(&solution_file))?; - let run_command = RunCommand::new( settings, &problem_path, - &runnable_file, + solution_file, problem_path.join("solutions/solution.out"), - problem_path.join(&solution_file), + problem_path.join(format!("{solution_file}")), )?; let test_files = get_input_files_in_directory(problem_path.join("tests"))?; From 4314e174b25db0065cc5c60d03e66e01ab73bc05 Mon Sep 17 00:00:00 2001 From: Ray Okamoto Date: Wed, 16 Jul 2025 14:42:52 +0930 Subject: [PATCH 3/4] feat(problem): add lang option for generate and fuzz --- crates/cli/src/cli/problem.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/crates/cli/src/cli/problem.rs b/crates/cli/src/cli/problem.rs index fa5e86a..b3ebf3e 100644 --- a/crates/cli/src/cli/problem.rs +++ b/crates/cli/src/cli/problem.rs @@ -79,6 +79,10 @@ pub fn cli() -> Command { .long("generator-file") .help("Name of the generator file") .action(ArgAction::Set), + Arg::new("generator-lang") + .long("generator-lang") + .help("Language of the generator file (e.g. cpp, py)") + .action(ArgAction::Set), Arg::new("problem") .short('p') .long("problem") @@ -94,6 +98,10 @@ pub fn cli() -> Command { .long("file") .help("Name of the generator file") .action(ArgAction::Set), + Arg::new("lang") + .long("lang") + .help("Language of the generator file (e.g. cpp, py)") + .action(ArgAction::Set), Arg::new("problem") .short('p') .long("problem") @@ -235,11 +243,12 @@ pub fn exec(args: &ArgMatches) -> Result<()> { solution_files.push(solution_file?); } + let generator_lang = cmd.try_get_one::("generator-lang")?; let generator = RunnableFile::new( &settings, RunnableCategory::Generator, cmd.try_get_one::("generator-file")?, - None, + generator_lang, )?; let fuzz_args = fuzz::FuzzArgs { @@ -257,11 +266,12 @@ pub fn exec(args: &ArgMatches) -> Result<()> { None => &get_problem_from_cwd(&problems_dir)?, }; + let generator_lang = cmd.try_get_one::("lang")?; let generator = RunnableFile::new( &settings, RunnableCategory::Generator, cmd.try_get_one::("file")?, - None, + generator_lang, )?; let test_name = cmd From d0d3650b88f28270f67f9d5c2baf7da7c6a9d15c Mon Sep 17 00:00:00 2001 From: Ray Okamoto Date: Wed, 16 Jul 2025 19:19:14 +0930 Subject: [PATCH 4/4] refactor: inline args --- crates/cli/src/cli/problem.rs | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/crates/cli/src/cli/problem.rs b/crates/cli/src/cli/problem.rs index b3ebf3e..e8fc17b 100644 --- a/crates/cli/src/cli/problem.rs +++ b/crates/cli/src/cli/problem.rs @@ -243,12 +243,11 @@ pub fn exec(args: &ArgMatches) -> Result<()> { solution_files.push(solution_file?); } - let generator_lang = cmd.try_get_one::("generator-lang")?; let generator = RunnableFile::new( &settings, RunnableCategory::Generator, cmd.try_get_one::("generator-file")?, - generator_lang, + cmd.try_get_one::("generator-lang")?, )?; let fuzz_args = fuzz::FuzzArgs { @@ -266,12 +265,11 @@ pub fn exec(args: &ArgMatches) -> Result<()> { None => &get_problem_from_cwd(&problems_dir)?, }; - let generator_lang = cmd.try_get_one::("lang")?; let generator = RunnableFile::new( &settings, RunnableCategory::Generator, cmd.try_get_one::("file")?, - generator_lang, + cmd.try_get_one::("lang")?, )?; let test_name = cmd @@ -293,14 +291,11 @@ pub fn exec(args: &ArgMatches) -> Result<()> { None => &get_problem_from_cwd(&problems_dir)?, }; - let solution_file = cmd.try_get_one::("file")?; - let solution_lang = cmd.try_get_one::("lang")?; - let solution_file = RunnableFile::new( &settings, RunnableCategory::Solution, - solution_file, - solution_lang, + cmd.try_get_one::("file")?, + cmd.try_get_one::("lang")?, )?; solve::solve(&settings, &problems_dir, problem_name, &solution_file)?; @@ -311,14 +306,11 @@ pub fn exec(args: &ArgMatches) -> Result<()> { None => &get_problem_from_cwd(&problems_dir)?, }; - let solution_file = cmd.try_get_one::("file")?; - let solution_lang = cmd.try_get_one::("lang")?; - let solution_file = RunnableFile::new( &settings, RunnableCategory::Solution, - solution_file, - solution_lang, + cmd.try_get_one::("file")?, + cmd.try_get_one::("lang")?, )?; test::test(&settings, &problems_dir, problem_name, &solution_file)?;