diff --git a/Cargo.lock b/Cargo.lock index cb2e27d..19dbfb2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,6 +6,7 @@ version = 3 name = "AutoGrader" version = "0.1.0" dependencies = [ + "ansi_term", "clap", "glob", "log", @@ -14,6 +15,7 @@ dependencies = [ "serde", "serde_json", "tokio", + "uuid", "walkdir", ] @@ -824,6 +826,15 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom", +] + [[package]] name = "vec_map" version = "0.8.2" diff --git a/Cargo.toml b/Cargo.toml index d517c27..80c14e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,8 @@ authors = ["Thanapat Chotipun "] edition = "2021" [dependencies] -clap = { version = "2.33.3", features =["yaml","color","suggestions"] } +ansi_term = "0.12" +clap = { version = "^2.34.0", features =["yaml","color","suggestions"] } glob = "0.3.0" log = { version = "0.4.14", features = ["std"] } rand = "0.8.4" @@ -14,4 +15,5 @@ serde = { version = "^1.0", features = ["derive"] } serde_json = "1.0" tokio = { version = "^1.13.0", features = ["full"] } postgres = "0.19.2" -walkdir = "2.3.2" \ No newline at end of file +uuid = { version = "0.8", features = ["v4"] } +walkdir = "2.3.2" diff --git a/src/cli.yaml b/src/cli.yaml deleted file mode 100644 index b9d2675..0000000 --- a/src/cli.yaml +++ /dev/null @@ -1,65 +0,0 @@ -name: AutoGrader -version: "0.0.1" -author: Thanapat Chotipun -args: - - verbose: - short: v - help: Verbosity level -subcommands: - - test: - short: t - about: "Test configuration file for AutoGrader" - args: - - INPUT: - takes_value: true - value_name: FILE - help: Config file to check - - run: - about: "Run single test file" - short: r - args: - - TEST: - short: t - takes_value: true - value_name: FILENAME - required: true - help: AutoGrader test config - - INPUT: - short: i - takes_value: true - value_name: FILENAME - multiple: true - required: true - help: File to be tested - - lang: - short: l - value_name: LANG - required: true - help: Input file language used - - err: - short: e - help: Display error message / AutoGrader messages - - serve: - about: "Run server mode (Multiple Test)" - short: s - args: - - dir: - short: d - required: true - value_name: config_dir - help: Config file for `AutoGrader Serve` - - port: - short: p - required: true - value_name: tcp_port - help: Port of AutoGrader Service - - logging: - short: l - value_name: LEVEL - help: "0 - No logging\n1 - Logging to file\n2 - Logging to STDOUT\n3 - Logging both file and STDOUT" - - config: - short: c - multiple: false - help: AutoGrader configuration file - takes_value: true - value_name: config diff --git a/src/main.rs b/src/main.rs index d5fa111..144b45d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,85 @@ #![allow(non_snake_case)] - -#[macro_use] -extern crate clap; -use clap::App; +use clap::{App, AppSettings, Arg, SubCommand}; +use ansi_term::Colour; mod modules; fn main() { - let yaml = load_yaml!("cli.yaml"); - let args = App::from_yaml(yaml).get_matches(); - if args.subcommand.is_none() {} - if let Some(_testing) = args.subcommand_matches("test") {} + let args = App::new("AutoGrader") + .setting(AppSettings::SubcommandRequiredElseHelp) + .setting(AppSettings::ColoredHelp) + .version("0.0.1") + .about("Programming Competition Scoreboard") + .author("Thanapat Chotipun ") + .subcommands([ + SubCommand::with_name("test") + .setting(AppSettings::ColoredHelp) + .about("Test configuration file for AutoGrader") + .arg( + Arg::with_name("input") + .required(true) + .takes_value(true) + .help("Config file to check") + .value_name("file"), + ), + SubCommand::with_name("run") + .setting(AppSettings::ColoredHelp) + .about("Run single test case and solution") + .args(&[ + Arg::with_name("test") + .short("t") + .takes_value(true) + .value_name("file") + .required(true) + .help("Autograder Test Config"), + Arg::with_name("input") + .short("i") + .takes_value(true) + .value_name("file") + .required(true) + .help("File to be tested"), + ]), + SubCommand::with_name("serve") + .setting(AppSettings::ColoredHelp) + .about("Run AutoGrader Session") + .args(&[Arg::with_name("config") + .short("c") + .required(true) + .takes_value(true) + .value_name("file") + .help("AutoGRader Session Config file"), + Arg::with_name("logginf").short("l") + .value_name("level") + .help("Set logging level") + .long_help("0 - No logging\n1 - Logging to file\n2 - Logging to STDOUT\n3 - Logging both file and STDOUT") + ] + ), + ]).get_matches(); + if let Some(_testing) = args.subcommand_matches("test") { + let filename = _testing.value_of("input").unwrap(); + match modules::parser::parse_ext(&filename) + .ok() + .unwrap_or([""].to_vec())[..] + { + ["test.", "json"] => { + println!("{} Parse as {} config....",Colour::Blue.bold().paint("\u{24D8}"),Colour::Purple.bold().paint("Session")); + if modules::config::parse_test_config(&filename).is_some() { + println!("{} This file is valid {} config",Colour::Green.bold().paint("\u{2714}"),Colour::Purple.bold().paint("Session")); + } else { + println!("{} Not valid!!!",Colour::Red.bold().paint("\u{2718}")); + } + } + ["json"] => { + println!("{} Parse as {} config....",Colour::Blue.bold().paint("\u{24D8}"),Colour::Purple.bold().paint("Session")); + if modules::config::parse_root_config(&filename).is_some() { + println!("{} This file is valid {} config",Colour::Green.bold().paint("\u{2714}"),Colour::Purple.bold().paint("Session")); + } else { + println!("{} Not valid!!!",Colour::Red.bold().paint("\u{2718}")); + } + } + _ => println!("This file isn't AutoGraderConfig file"), + } + } if let Some(_run) = args.subcommand_matches("run") {} if let Some(_serve) = args.subcommand_matches("serve") {} } diff --git a/src/modules/config.rs b/src/modules/config.rs index 44a7738..7c69c0d 100644 --- a/src/modules/config.rs +++ b/src/modules/config.rs @@ -66,14 +66,14 @@ pub struct TestConfig { limit: Option, } -pub fn parse_root_config(filename: &str) -> AutoGraderConfig { +pub fn parse_root_config(filename: &str) -> Option { let buffer = std::fs::read_to_string(filename); match buffer { Ok(d) => match from_str::(&d) { - Ok(d) => d, - Err(e) => panic!("Config file cannot parse. Error: {}", e), + Ok(d) => Some(d), + Err(_) => None, }, - Err(e) => panic!("Config file not found. Error: {}", e), + Err(_) => None, } } diff --git a/src/modules/parser.rs b/src/modules/parser.rs index e28b5c6..b1de265 100644 --- a/src/modules/parser.rs +++ b/src/modules/parser.rs @@ -1,27 +1,54 @@ +use uuid::Uuid; use walkdir::WalkDir; -pub fn parse_ext(filename: &str) -> Result<&str, &str> { - match filename.split_inclusive(".").last() { - Some(ext) => Ok(ext), - None => Err("No file extension found in file"), +pub struct Test { + id: Uuid, + cases: Option>, + score: u32, +} + +pub struct TestCase { + id: Uuid, + input: String, + output: Option, + score: Option, +} + +pub fn parse_ext(filename: &str) -> Result, &str> { + let sep: Vec<&str> = filename.split_inclusive(".").collect(); + if sep.len() > 0 { + return Ok(sep[1..].to_vec()); + } else { + return Err("No file extension found."); } } -pub fn load_input(filename: &str) -> Option { +pub fn load_testcase(filename: &str) -> Option { let buffer = std::fs::read_to_string(filename); match buffer { - Ok(data) => Some(data), + Ok(data) => Some(TestCase { + id: Uuid::new_v4(), + input: data, + output: None, + score: None, + }), Err(_) => None, } } -pub fn find_input(dirname: &str) -> Option> { - let mut input_streams: Vec = vec![]; +pub fn find_testcases(dirname: &str) -> Option> { + let mut input_streams: Vec = vec![]; for e in WalkDir::new(dirname).into_iter().filter_map(|e| e.ok()) { if e.metadata().unwrap().is_file() { let filepath = e.path().display().to_string(); - if parse_ext(&filepath).ok() == Some("txt") { - match load_input(&filepath) { + if parse_ext(&filepath) + .ok() + .unwrap_or([""].to_vec()) + .last() + .unwrap() + == &"txt" + { + match load_testcase(&filepath) { Some(d) => input_streams.push(d), None => (), } @@ -36,12 +63,17 @@ pub fn find_input(dirname: &str) -> Option> { } #[cfg(test)] +#[test] +fn parse_config_and_input_files() { + assert_eq!(parse_ext("tests/config.json").unwrap(),["json"].to_vec()); +} + #[test] fn read_input_file() { - assert!(load_input("tests/sum/sum1.txt").is_some()); + assert!(load_testcase("tests/sum/sum1.txt").is_some()); } #[test] fn find_input_cases() { - assert!(find_input("tests/sum").is_some()); + assert!(find_testcases("tests/sum").is_some()); } diff --git a/src/modules/tester.rs b/src/modules/tester.rs index 8b13789..e69de29 100644 --- a/src/modules/tester.rs +++ b/src/modules/tester.rs @@ -1 +0,0 @@ - diff --git a/tests/sum/sum.cpp b/tests/sum/sum.cpp index 8d79f0f..e7d503e 100644 --- a/tests/sum/sum.cpp +++ b/tests/sum/sum.cpp @@ -1,5 +1,8 @@ #include int main() { + int n1,n2; + std::cin >> n1 >> n2; + std::cout << n1+n2 << std::endl; return 0; } \ No newline at end of file diff --git a/tests/sum/sum1.txt b/tests/sum/sum1.txt index bb7909b..69010fb 100644 --- a/tests/sum/sum1.txt +++ b/tests/sum/sum1.txt @@ -1 +1 @@ -5 2 3 4 5 +5 2 diff --git a/tests/sum/sum2.txt b/tests/sum/sum2.txt index 0ba16c1..0d6a524 100644 --- a/tests/sum/sum2.txt +++ b/tests/sum/sum2.txt @@ -1 +1 @@ -1 5 2 4 5 +1 5