From 06b80b2f2bc753e172743280799e1ff0f7d21148 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roger=20D=C3=ADaz=20Vi=C3=B1olas?= Date: Sun, 20 Feb 2022 18:39:44 +0100 Subject: [PATCH 1/2] First steps towards the resource based system --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/compilation/compiler.rs | 17 +++++++ src/fetch/connection_manager.rs | 40 ++++++++------- src/fetch/download.rs | 60 ----------------------- src/fetch/mod.rs | 70 ++++++++++---------------- src/fetch/resource.rs | 87 +++++++++++++++++++++++++++++++++ src/lib.rs | 4 +- src/testing/mod.rs | 18 +++++++ 9 files changed, 173 insertions(+), 127 deletions(-) delete mode 100644 src/fetch/download.rs create mode 100644 src/fetch/resource.rs diff --git a/Cargo.lock b/Cargo.lock index 3490707..f004fa7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,7 +10,7 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "advocat" -version = "3.0.0" +version = "3.1.0-dev" dependencies = [ "configparser", "curl", diff --git a/Cargo.toml b/Cargo.toml index 268fcbb..dcbddec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "advocat" description = "A complimentary CLI tool to jutge.org" -version = "3.0.0" +version = "3.1.0-dev" license="GPL-3.0" authors = ["Roger Díaz Viñolas "] readme = "README.md" diff --git a/src/compilation/compiler.rs b/src/compilation/compiler.rs index 76fe1fa..7d41719 100644 --- a/src/compilation/compiler.rs +++ b/src/compilation/compiler.rs @@ -1,5 +1,8 @@ use crate::{debug, problem}; use std::{fmt, io, path, process}; +use crate::fetch::connection_manager::ConnectionManager; +use crate::fetch::resource; +use crate::fetch::resource::Resource; pub static P1XX: Compiler = Compiler { command: "g++", @@ -147,4 +150,18 @@ impl Compiler<'_> { self.compile_and_link_second_pass(generated_source, problem.output.as_path()) .map_err(|error| CompileProcessError { pass: 2, error }) } + + pub fn write_required_resources<'a>(&self, problem: &problem::Problem, connection: &'a ConnectionManager, resources: &mut Vec>) { + if !problem.has_main { + let main_cc = resource::OnlineResource::new( + problem.work_dir.join("main.cc"), + problem.main_cc_url.clone(), + connection + ); + resources.push(Box::new(main_cc)); + } + + let user_source = resource::UserResource::new(problem.source.to_path_buf()); + resources.push(Box::new(user_source)); + } } diff --git a/src/fetch/connection_manager.rs b/src/fetch/connection_manager.rs index 3b8f9b6..0090c1f 100644 --- a/src/fetch/connection_manager.rs +++ b/src/fetch/connection_manager.rs @@ -3,6 +3,7 @@ use crate::{config, debug, warning}; use curl::easy; use std::io::Write; use std::{fmt, fs, io, path}; +use std::cell::RefCell; pub enum Error { CurlError(curl::Error), @@ -33,7 +34,7 @@ impl From for Error { } pub struct ConnectionManager { - handle: easy::Easy, + handle: RefCell, } impl ConnectionManager { @@ -46,7 +47,7 @@ impl ConnectionManager { let mut handle = easy::Easy::new(); handle.cookie_file(cookie_store.as_path())?; handle.cookie_jar(cookie_store.as_path())?; - let mut cm = ConnectionManager { handle }; + let cm = ConnectionManager { handle: RefCell::new(handle) }; if cm.check_is_authenticated()? { debug!("Client is authenticated, reusing previous session") @@ -64,16 +65,16 @@ impl ConnectionManager { Ok(cm) } - pub fn get_file(&mut self, url: &str, path: &path::Path) -> Result<(), Error> { + pub fn get_file(&self, url: &str, path: &path::Path) -> Result<(), Error> { debug!("Downloading {} to {}", url, path.to_string_lossy()); + let mut handle = self.handle.borrow_mut(); let mut file = fs::File::create(path)?; - self.handle.url(url)?; - self.handle - .write_function(move |data| file.write(data).or(Ok(0)))?; - self.handle.perform()?; + handle.url(url)?; + handle.write_function(move |data| file.write(data).or(Ok(0)))?; + handle.perform()?; - if let Some(content_type) = self.handle.content_type()? { + if let Some(content_type) = handle.content_type()? { if content_type.contains("html") { fs::remove_file(path)?; return Err(Error::AuthError); @@ -84,17 +85,19 @@ impl ConnectionManager { } fn try_to_authenticate( - &mut self, + &self, credentials: &credentials::Credentials, ) -> Result { if let Some(form) = credentials.build_form() { debug!("Attempting to authenticate"); - self.handle.url("https://jutge.org/")?; - self.handle.nobody(true)?; - self.handle.httppost(form)?; - self.handle.perform()?; - self.handle.nobody(false)?; + let mut handle = self.handle.borrow_mut(); + handle.url("https://jutge.org/")?; + handle.nobody(true)?; + handle.httppost(form)?; + handle.perform()?; + handle.nobody(false)?; debug!("Authentication finished"); + drop(handle); // We can't check authentication without borrowing the handle, so we drop it here self.check_is_authenticated() } else { debug!("Unable to generate the authentication form"); @@ -102,12 +105,13 @@ impl ConnectionManager { } } - fn check_is_authenticated(&mut self) -> Result { + fn check_is_authenticated(&self) -> Result { let mut response = Vec::new(); + let mut handle = self.handle.borrow_mut(); - self.handle.url("https://jutge.org/dashboard")?; + handle.url("https://jutge.org/dashboard")?; { - let mut transfer = self.handle.transfer(); + let mut transfer = handle.transfer(); transfer.write_function(|data| { response.extend_from_slice(data); Ok(data.len()) @@ -117,4 +121,4 @@ impl ConnectionManager { Ok(!String::from_utf8_lossy(&response).contains("Did you sign in?")) } -} +} \ No newline at end of file diff --git a/src/fetch/download.rs b/src/fetch/download.rs deleted file mode 100644 index 0b6b617..0000000 --- a/src/fetch/download.rs +++ /dev/null @@ -1,60 +0,0 @@ -use crate::fetch::{connection_manager, unzip}; -use crate::{debug, problem, ux}; - -pub fn download_problem_zip( - problem: &problem::Problem, - connection: &mut connection_manager::ConnectionManager, -) -> (ux::TaskStatus, Option) { - let path = problem.work_dir.join("problem.zip"); - - if path.is_file() { - debug!("Problem zip already downloaded"); - (ux::TaskStatus::SkipGood, None) - } else if path.is_dir() { - debug!("The download path is a folder"); - (ux::TaskStatus::SkipBad, None) - } else { - match connection.get_file(&problem.zip_url, &path) { - Ok(()) => (ux::TaskStatus::Done, None), - Err(e) => (ux::TaskStatus::Fail, Some(e)), - } - } -} - -pub fn download_problem_main( - problem: &problem::Problem, - connection: &mut connection_manager::ConnectionManager, -) -> (ux::TaskStatus, Option) { - let path = problem.work_dir.join("main.cc"); - - if problem.has_main || path.is_file() { - debug!("Problem main.cc already downloaded or unnecessary"); - (ux::TaskStatus::SkipGood, None) - } else if path.is_dir() { - debug!("The download path is a folder"); - (ux::TaskStatus::SkipBad, None) - } else { - match connection.get_file(&problem.main_cc_url, &path) { - Ok(()) => (ux::TaskStatus::Done, None), - Err(e) => (ux::TaskStatus::Fail, Some(e)), - } - } -} - -pub fn unzip_problem_tests(problem: &problem::Problem) -> (ux::TaskStatus, Option) { - let zip_path = problem.work_dir.join("problem.zip"); - let tests_path = problem.work_dir.join("samples"); - - if tests_path.is_dir() { - debug!("Problem tests already extracted"); - (ux::TaskStatus::SkipGood, None) - } else if tests_path.is_file() || !zip_path.exists() { - debug!("Unable to extract problem tests"); - (ux::TaskStatus::SkipBad, None) - } else { - match unzip::unzip_samples(&zip_path, &tests_path) { - Ok(()) => (ux::TaskStatus::Done, None), - Err(e) => (ux::TaskStatus::Fail, Some(e)), - } - } -} diff --git a/src/fetch/mod.rs b/src/fetch/mod.rs index fc9f5da..aea3269 100644 --- a/src/fetch/mod.rs +++ b/src/fetch/mod.rs @@ -1,63 +1,43 @@ -use crate::{config, error, problem, ux, warning}; -use std::fmt; +use crate::{compilation, config, error, problem, testing}; -mod connection_manager; +pub mod connection_manager; mod credentials; -mod download; mod unzip; +pub mod resource; pub use credentials::Credentials; +use crate::fetch::resource::Resource; pub fn fetch_resources( problem: &problem::Problem, config: &config::Config, -) -> Result<(bool, bool, bool), crate::Error> { - let mut connection = +) -> Result<(bool, bool), crate::Error> { + let connection = connection_manager::ConnectionManager::new(config).map_err(|e| crate::Error { description: format!("Couldn't start the connection manager: {}", e), exitcode: exitcode::IOERR, })?; - let zip = execute_task("Downloading problem zip", || { - download::download_problem_zip(problem, &mut connection) - }); - let main_cc = execute_task("Downloading problem main.cc", || { - download::download_problem_main(problem, &mut connection) - }); - let tests = execute_task("Extracting tests", || { - download::unzip_problem_tests(problem) - }); - - if !zip { - warning!("Unable to retrieve tests!"); - } - - if !main_cc { - return Err(crate::Error { - description: String::from( - "Unable to retrieve the main.cc file, which is required to compile your binary!", - ), - exitcode: exitcode::IOERR, - }); + let mut compilation_resources: Vec> = Vec::new(); + compilation::P1XX.write_required_resources(problem, &connection, &mut compilation_resources); + let mut testing_resources: Vec> = Vec::new(); + testing::write_required_resources(problem, &connection, &mut testing_resources); + + let mut compilation = true; + for resource in compilation_resources { + if let Err(e) = resource.acquire() { + error!("Compilation resource acquisition failed: {}", e); + compilation = false; + } } - if !tests { - warning!("Unable to unzip tests!"); + let mut testing = true; + for resource in testing_resources { + if let Err(e) = resource.acquire() { + error!("Testing resource acquisition failed: {}", e); + testing = false; + } } - Ok((zip, main_cc, tests)) -} - -fn execute_task(name: &str, mut task: T) -> bool -where - T: FnMut() -> (ux::TaskStatus, Option), -{ - ux::show_task_status(name, ux::TaskType::Fetch, &ux::TaskStatus::InProgress); - let (status, err) = task(); - - ux::show_task_status(name, ux::TaskType::Fetch, &status); - if let Some(err) = err { - error!("The task [{}] returned the following error: {}", name, err); - } - status.is_ok() -} + Ok((compilation, testing)) +} \ No newline at end of file diff --git a/src/fetch/resource.rs b/src/fetch/resource.rs new file mode 100644 index 0000000..b6973c4 --- /dev/null +++ b/src/fetch/resource.rs @@ -0,0 +1,87 @@ +use std::path; +use crate::fetch::connection_manager::ConnectionManager; +use crate::fetch::unzip; + +pub trait Resource { + fn acquire(&self) -> Result; +} + +pub struct UserResource { + path: path::PathBuf +} + +impl UserResource { + pub fn new(path: path::PathBuf) -> UserResource { + UserResource { path } + } +} + +impl Resource for UserResource { + fn acquire(&self) -> Result { + if self.path.exists() { + Ok(self.path.to_path_buf()) + } else { + Err(String::from("The path doesn't exist")) + } + } +} + +pub struct OnlineResource<'a> { + download_path: path::PathBuf, + connection_manager: &'a ConnectionManager, + url: String +} + +impl<'a> OnlineResource<'a> { + pub fn new(download_path: path::PathBuf, url: String, connection_manager: &ConnectionManager) -> OnlineResource { + OnlineResource { + download_path, + url, + connection_manager + } + } +} + +impl<'a> Resource for OnlineResource<'a> { + fn acquire(&self) -> Result { + if self.download_path.is_file() { + Ok(self.download_path.to_path_buf()) + } else if self.download_path.is_dir() { + Err(String::from("The download path is a directory")) + } else { + self.connection_manager.get_file(&self.url, &self.download_path) + .map_err(|e| e.to_string())?; + Ok(self.download_path.to_path_buf()) + } + } +} + +pub struct UnzipResource { + output_path: path::PathBuf, + source_resource: T +} + +impl UnzipResource { + pub fn new(source_resource: T, output_path: path::PathBuf) -> UnzipResource { + UnzipResource { + source_resource, + output_path + } + } +} + +impl Resource for UnzipResource { + fn acquire(&self) -> Result { + if self.output_path.is_dir() { + Ok(self.output_path.to_path_buf()) + } else if self.output_path.is_file() { + Err(String::from("The extraction path is a file")) + } else { + let source = self.source_resource.acquire()?; + unzip::unzip_samples(source.as_path(), self.output_path.as_path()) + .map_err(|e| e.to_string())?; + Ok(self.output_path.to_path_buf()) + } + + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index ff08179..f7e61c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,13 +47,13 @@ pub fn run() -> Result { let problem = Problem::new(&config)?; debug!("Done! Problem details: {:?}", problem); - let (_zip, _main_cc, tests) = fetch::fetch_resources(&problem, &config)?; + let (_compilation_resources, testing_resources) = fetch::fetch_resources(&problem, &config)?; let tests = [ load_tests( "jutge.org", problem.work_dir.join("samples").as_path(), - !tests, + !testing_resources, ), load_tests("user", problem.source.parent().unwrap(), false), ]; diff --git a/src/testing/mod.rs b/src/testing/mod.rs index 32f1035..0b01a57 100644 --- a/src/testing/mod.rs +++ b/src/testing/mod.rs @@ -4,3 +4,21 @@ mod testsuite; pub use testsuite::Error; pub use testsuite::TestSuite; +use crate::fetch::connection_manager::ConnectionManager; +use crate::fetch::resource; +use crate::fetch::resource::{Resource}; +use crate::problem; + +pub fn write_required_resources<'a>(problem: &problem::Problem, connection: &'a ConnectionManager, resources: &mut Vec>) { + let zip = resource::OnlineResource::new( + problem.work_dir.join("problem.zip"), + problem.zip_url.clone(), + connection + ); + + let tests = resource::UnzipResource::new( + zip, + problem.work_dir.join("samples") + ); + resources.push(Box::new(tests)); +} \ No newline at end of file From 63f656bfd26f7322b71e9c2fc1922998d5f2b030 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roger=20D=C3=ADaz=20Vi=C3=B1olas?= Date: Sun, 20 Feb 2022 18:39:44 +0100 Subject: [PATCH 2/2] First steps towards the resource based system --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/compilation/compiler.rs | 17 +++++++ src/fetch/connection_manager.rs | 40 ++++++++------- src/fetch/download.rs | 60 ----------------------- src/fetch/mod.rs | 70 ++++++++++---------------- src/fetch/resource.rs | 87 +++++++++++++++++++++++++++++++++ src/lib.rs | 4 +- src/testing/mod.rs | 18 +++++++ 9 files changed, 173 insertions(+), 127 deletions(-) delete mode 100644 src/fetch/download.rs create mode 100644 src/fetch/resource.rs diff --git a/Cargo.lock b/Cargo.lock index 3490707..f004fa7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,7 +10,7 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "advocat" -version = "3.0.0" +version = "3.1.0-dev" dependencies = [ "configparser", "curl", diff --git a/Cargo.toml b/Cargo.toml index 268fcbb..dcbddec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "advocat" description = "A complimentary CLI tool to jutge.org" -version = "3.0.0" +version = "3.1.0-dev" license="GPL-3.0" authors = ["Roger Díaz Viñolas "] readme = "README.md" diff --git a/src/compilation/compiler.rs b/src/compilation/compiler.rs index 76fe1fa..7d41719 100644 --- a/src/compilation/compiler.rs +++ b/src/compilation/compiler.rs @@ -1,5 +1,8 @@ use crate::{debug, problem}; use std::{fmt, io, path, process}; +use crate::fetch::connection_manager::ConnectionManager; +use crate::fetch::resource; +use crate::fetch::resource::Resource; pub static P1XX: Compiler = Compiler { command: "g++", @@ -147,4 +150,18 @@ impl Compiler<'_> { self.compile_and_link_second_pass(generated_source, problem.output.as_path()) .map_err(|error| CompileProcessError { pass: 2, error }) } + + pub fn write_required_resources<'a>(&self, problem: &problem::Problem, connection: &'a ConnectionManager, resources: &mut Vec>) { + if !problem.has_main { + let main_cc = resource::OnlineResource::new( + problem.work_dir.join("main.cc"), + problem.main_cc_url.clone(), + connection + ); + resources.push(Box::new(main_cc)); + } + + let user_source = resource::UserResource::new(problem.source.to_path_buf()); + resources.push(Box::new(user_source)); + } } diff --git a/src/fetch/connection_manager.rs b/src/fetch/connection_manager.rs index 3b8f9b6..0090c1f 100644 --- a/src/fetch/connection_manager.rs +++ b/src/fetch/connection_manager.rs @@ -3,6 +3,7 @@ use crate::{config, debug, warning}; use curl::easy; use std::io::Write; use std::{fmt, fs, io, path}; +use std::cell::RefCell; pub enum Error { CurlError(curl::Error), @@ -33,7 +34,7 @@ impl From for Error { } pub struct ConnectionManager { - handle: easy::Easy, + handle: RefCell, } impl ConnectionManager { @@ -46,7 +47,7 @@ impl ConnectionManager { let mut handle = easy::Easy::new(); handle.cookie_file(cookie_store.as_path())?; handle.cookie_jar(cookie_store.as_path())?; - let mut cm = ConnectionManager { handle }; + let cm = ConnectionManager { handle: RefCell::new(handle) }; if cm.check_is_authenticated()? { debug!("Client is authenticated, reusing previous session") @@ -64,16 +65,16 @@ impl ConnectionManager { Ok(cm) } - pub fn get_file(&mut self, url: &str, path: &path::Path) -> Result<(), Error> { + pub fn get_file(&self, url: &str, path: &path::Path) -> Result<(), Error> { debug!("Downloading {} to {}", url, path.to_string_lossy()); + let mut handle = self.handle.borrow_mut(); let mut file = fs::File::create(path)?; - self.handle.url(url)?; - self.handle - .write_function(move |data| file.write(data).or(Ok(0)))?; - self.handle.perform()?; + handle.url(url)?; + handle.write_function(move |data| file.write(data).or(Ok(0)))?; + handle.perform()?; - if let Some(content_type) = self.handle.content_type()? { + if let Some(content_type) = handle.content_type()? { if content_type.contains("html") { fs::remove_file(path)?; return Err(Error::AuthError); @@ -84,17 +85,19 @@ impl ConnectionManager { } fn try_to_authenticate( - &mut self, + &self, credentials: &credentials::Credentials, ) -> Result { if let Some(form) = credentials.build_form() { debug!("Attempting to authenticate"); - self.handle.url("https://jutge.org/")?; - self.handle.nobody(true)?; - self.handle.httppost(form)?; - self.handle.perform()?; - self.handle.nobody(false)?; + let mut handle = self.handle.borrow_mut(); + handle.url("https://jutge.org/")?; + handle.nobody(true)?; + handle.httppost(form)?; + handle.perform()?; + handle.nobody(false)?; debug!("Authentication finished"); + drop(handle); // We can't check authentication without borrowing the handle, so we drop it here self.check_is_authenticated() } else { debug!("Unable to generate the authentication form"); @@ -102,12 +105,13 @@ impl ConnectionManager { } } - fn check_is_authenticated(&mut self) -> Result { + fn check_is_authenticated(&self) -> Result { let mut response = Vec::new(); + let mut handle = self.handle.borrow_mut(); - self.handle.url("https://jutge.org/dashboard")?; + handle.url("https://jutge.org/dashboard")?; { - let mut transfer = self.handle.transfer(); + let mut transfer = handle.transfer(); transfer.write_function(|data| { response.extend_from_slice(data); Ok(data.len()) @@ -117,4 +121,4 @@ impl ConnectionManager { Ok(!String::from_utf8_lossy(&response).contains("Did you sign in?")) } -} +} \ No newline at end of file diff --git a/src/fetch/download.rs b/src/fetch/download.rs deleted file mode 100644 index 0b6b617..0000000 --- a/src/fetch/download.rs +++ /dev/null @@ -1,60 +0,0 @@ -use crate::fetch::{connection_manager, unzip}; -use crate::{debug, problem, ux}; - -pub fn download_problem_zip( - problem: &problem::Problem, - connection: &mut connection_manager::ConnectionManager, -) -> (ux::TaskStatus, Option) { - let path = problem.work_dir.join("problem.zip"); - - if path.is_file() { - debug!("Problem zip already downloaded"); - (ux::TaskStatus::SkipGood, None) - } else if path.is_dir() { - debug!("The download path is a folder"); - (ux::TaskStatus::SkipBad, None) - } else { - match connection.get_file(&problem.zip_url, &path) { - Ok(()) => (ux::TaskStatus::Done, None), - Err(e) => (ux::TaskStatus::Fail, Some(e)), - } - } -} - -pub fn download_problem_main( - problem: &problem::Problem, - connection: &mut connection_manager::ConnectionManager, -) -> (ux::TaskStatus, Option) { - let path = problem.work_dir.join("main.cc"); - - if problem.has_main || path.is_file() { - debug!("Problem main.cc already downloaded or unnecessary"); - (ux::TaskStatus::SkipGood, None) - } else if path.is_dir() { - debug!("The download path is a folder"); - (ux::TaskStatus::SkipBad, None) - } else { - match connection.get_file(&problem.main_cc_url, &path) { - Ok(()) => (ux::TaskStatus::Done, None), - Err(e) => (ux::TaskStatus::Fail, Some(e)), - } - } -} - -pub fn unzip_problem_tests(problem: &problem::Problem) -> (ux::TaskStatus, Option) { - let zip_path = problem.work_dir.join("problem.zip"); - let tests_path = problem.work_dir.join("samples"); - - if tests_path.is_dir() { - debug!("Problem tests already extracted"); - (ux::TaskStatus::SkipGood, None) - } else if tests_path.is_file() || !zip_path.exists() { - debug!("Unable to extract problem tests"); - (ux::TaskStatus::SkipBad, None) - } else { - match unzip::unzip_samples(&zip_path, &tests_path) { - Ok(()) => (ux::TaskStatus::Done, None), - Err(e) => (ux::TaskStatus::Fail, Some(e)), - } - } -} diff --git a/src/fetch/mod.rs b/src/fetch/mod.rs index fc9f5da..aea3269 100644 --- a/src/fetch/mod.rs +++ b/src/fetch/mod.rs @@ -1,63 +1,43 @@ -use crate::{config, error, problem, ux, warning}; -use std::fmt; +use crate::{compilation, config, error, problem, testing}; -mod connection_manager; +pub mod connection_manager; mod credentials; -mod download; mod unzip; +pub mod resource; pub use credentials::Credentials; +use crate::fetch::resource::Resource; pub fn fetch_resources( problem: &problem::Problem, config: &config::Config, -) -> Result<(bool, bool, bool), crate::Error> { - let mut connection = +) -> Result<(bool, bool), crate::Error> { + let connection = connection_manager::ConnectionManager::new(config).map_err(|e| crate::Error { description: format!("Couldn't start the connection manager: {}", e), exitcode: exitcode::IOERR, })?; - let zip = execute_task("Downloading problem zip", || { - download::download_problem_zip(problem, &mut connection) - }); - let main_cc = execute_task("Downloading problem main.cc", || { - download::download_problem_main(problem, &mut connection) - }); - let tests = execute_task("Extracting tests", || { - download::unzip_problem_tests(problem) - }); - - if !zip { - warning!("Unable to retrieve tests!"); - } - - if !main_cc { - return Err(crate::Error { - description: String::from( - "Unable to retrieve the main.cc file, which is required to compile your binary!", - ), - exitcode: exitcode::IOERR, - }); + let mut compilation_resources: Vec> = Vec::new(); + compilation::P1XX.write_required_resources(problem, &connection, &mut compilation_resources); + let mut testing_resources: Vec> = Vec::new(); + testing::write_required_resources(problem, &connection, &mut testing_resources); + + let mut compilation = true; + for resource in compilation_resources { + if let Err(e) = resource.acquire() { + error!("Compilation resource acquisition failed: {}", e); + compilation = false; + } } - if !tests { - warning!("Unable to unzip tests!"); + let mut testing = true; + for resource in testing_resources { + if let Err(e) = resource.acquire() { + error!("Testing resource acquisition failed: {}", e); + testing = false; + } } - Ok((zip, main_cc, tests)) -} - -fn execute_task(name: &str, mut task: T) -> bool -where - T: FnMut() -> (ux::TaskStatus, Option), -{ - ux::show_task_status(name, ux::TaskType::Fetch, &ux::TaskStatus::InProgress); - let (status, err) = task(); - - ux::show_task_status(name, ux::TaskType::Fetch, &status); - if let Some(err) = err { - error!("The task [{}] returned the following error: {}", name, err); - } - status.is_ok() -} + Ok((compilation, testing)) +} \ No newline at end of file diff --git a/src/fetch/resource.rs b/src/fetch/resource.rs new file mode 100644 index 0000000..b6973c4 --- /dev/null +++ b/src/fetch/resource.rs @@ -0,0 +1,87 @@ +use std::path; +use crate::fetch::connection_manager::ConnectionManager; +use crate::fetch::unzip; + +pub trait Resource { + fn acquire(&self) -> Result; +} + +pub struct UserResource { + path: path::PathBuf +} + +impl UserResource { + pub fn new(path: path::PathBuf) -> UserResource { + UserResource { path } + } +} + +impl Resource for UserResource { + fn acquire(&self) -> Result { + if self.path.exists() { + Ok(self.path.to_path_buf()) + } else { + Err(String::from("The path doesn't exist")) + } + } +} + +pub struct OnlineResource<'a> { + download_path: path::PathBuf, + connection_manager: &'a ConnectionManager, + url: String +} + +impl<'a> OnlineResource<'a> { + pub fn new(download_path: path::PathBuf, url: String, connection_manager: &ConnectionManager) -> OnlineResource { + OnlineResource { + download_path, + url, + connection_manager + } + } +} + +impl<'a> Resource for OnlineResource<'a> { + fn acquire(&self) -> Result { + if self.download_path.is_file() { + Ok(self.download_path.to_path_buf()) + } else if self.download_path.is_dir() { + Err(String::from("The download path is a directory")) + } else { + self.connection_manager.get_file(&self.url, &self.download_path) + .map_err(|e| e.to_string())?; + Ok(self.download_path.to_path_buf()) + } + } +} + +pub struct UnzipResource { + output_path: path::PathBuf, + source_resource: T +} + +impl UnzipResource { + pub fn new(source_resource: T, output_path: path::PathBuf) -> UnzipResource { + UnzipResource { + source_resource, + output_path + } + } +} + +impl Resource for UnzipResource { + fn acquire(&self) -> Result { + if self.output_path.is_dir() { + Ok(self.output_path.to_path_buf()) + } else if self.output_path.is_file() { + Err(String::from("The extraction path is a file")) + } else { + let source = self.source_resource.acquire()?; + unzip::unzip_samples(source.as_path(), self.output_path.as_path()) + .map_err(|e| e.to_string())?; + Ok(self.output_path.to_path_buf()) + } + + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index ff08179..f7e61c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,13 +47,13 @@ pub fn run() -> Result { let problem = Problem::new(&config)?; debug!("Done! Problem details: {:?}", problem); - let (_zip, _main_cc, tests) = fetch::fetch_resources(&problem, &config)?; + let (_compilation_resources, testing_resources) = fetch::fetch_resources(&problem, &config)?; let tests = [ load_tests( "jutge.org", problem.work_dir.join("samples").as_path(), - !tests, + !testing_resources, ), load_tests("user", problem.source.parent().unwrap(), false), ]; diff --git a/src/testing/mod.rs b/src/testing/mod.rs index 32f1035..0b01a57 100644 --- a/src/testing/mod.rs +++ b/src/testing/mod.rs @@ -4,3 +4,21 @@ mod testsuite; pub use testsuite::Error; pub use testsuite::TestSuite; +use crate::fetch::connection_manager::ConnectionManager; +use crate::fetch::resource; +use crate::fetch::resource::{Resource}; +use crate::problem; + +pub fn write_required_resources<'a>(problem: &problem::Problem, connection: &'a ConnectionManager, resources: &mut Vec>) { + let zip = resource::OnlineResource::new( + problem.work_dir.join("problem.zip"), + problem.zip_url.clone(), + connection + ); + + let tests = resource::UnzipResource::new( + zip, + problem.work_dir.join("samples") + ); + resources.push(Box::new(tests)); +} \ No newline at end of file