diff --git a/Cargo.lock b/Cargo.lock index edac3fd..b2f117b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -141,9 +141,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.2.4" +version = "1.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9157bbaa6b165880c27a4293a474c91cdcf265cc68cc829bf10be0964a391caf" +checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333" dependencies = [ "shlex", ] @@ -234,7 +234,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", ] [[package]] @@ -352,7 +352,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", ] [[package]] @@ -379,7 +379,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", ] [[package]] @@ -535,7 +535,7 @@ checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "http-mini" -version = "0.1.10" +version = "0.2.0" dependencies = [ "grcov", ] @@ -637,9 +637,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.168" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libm" @@ -697,9 +697,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" dependencies = [ "adler2", ] @@ -810,7 +810,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", ] [[package]] @@ -927,9 +927,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -1085,14 +1085,14 @@ checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", ] [[package]] name = "serde_json" -version = "1.0.133" +version = "1.0.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" dependencies = [ "itoa", "memchr", @@ -1164,9 +1164,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "symbolic-common" -version = "12.12.3" +version = "12.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ba5365997a4e375660bed52f5b42766475d5bc8ceb1bb13fea09c469ea0f49" +checksum = "cd33e73f154e36ec223c18013f7064a2c120f1162fc086ac9933542def186b00" dependencies = [ "debugid", "memmap2", @@ -1176,9 +1176,9 @@ dependencies = [ [[package]] name = "symbolic-demangle" -version = "12.12.3" +version = "12.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beff338b2788519120f38c59ff4bb15174f52a183e547bac3d6072c2c0aa48aa" +checksum = "89e51191290147f071777e37fe111800bb82a9059f9c95b19d2dd41bfeddf477" dependencies = [ "cpp_demangle", "msvc-demangler", @@ -1199,9 +1199,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.90" +version = "2.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +checksum = "70ae51629bf965c5c098cc9e87908a3df5301051a9e087d6f9bef5c9771ed126" dependencies = [ "proc-macro2", "quote", @@ -1287,22 +1287,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.7" +version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93605438cbd668185516ab499d589afb7ee1859ea3d5fc8f6b0755e1c7443767" +checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.7" +version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d8749b4531af2117677a5fcd12b1348a3fe2b81e36e61ffeac5c4aa3273e36" +checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", ] [[package]] @@ -1476,7 +1476,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", "wasm-bindgen-shared", ] @@ -1498,7 +1498,7 @@ checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1618,14 +1618,14 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.92", ] [[package]] name = "zip" -version = "2.2.1" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d52293fc86ea7cf13971b3bb81eb21683636e7ae24c729cdaf1b7c4157a352" +checksum = "ae9c1ea7b3a5e1f4b922ff856a129881167511563dc219869afe3787fc0c1a45" dependencies = [ "arbitrary", "crc32fast", diff --git a/Cargo.toml b/Cargo.toml index 8822583..64c606f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "http-mini" -version = "0.1.10" +version = "0.2.0" edition = "2021" description = "Mini HTTP server" license = "MIT" @@ -14,7 +14,7 @@ keywords = [ authors = ["Iotu Nicolae "] homepage = "https://github.com/NicolaeIotu/http-mini" repository = "https://github.com/NicolaeIotu/http-mini" -documentation = "https://docs.rs/crate/http-mini/latest" +documentation = "https://docs.rs/http-mini" categories = ["web-programming::http-server"] include = [ "/Cargo.toml", @@ -23,10 +23,6 @@ include = [ "/src/**", "/tasks/**", ] -exclude = [ - "/generated/**", - "/target/**", -] [dependencies] diff --git a/README.md b/README.md index 31a85f1..e3ea18d 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,13 @@ -# Mini HTTP server +# HTTP-Mini server -A simple single threaded server which can be used during development, or for light traffic applications. +A simple HTTP server which can be used during development, or for light traffic applications. -The application will only serve content which is located in the same directory as own executable. -This includes any content in subdirectories. +The application will only serve content in a target directory which must be specified at startup. By default, the application starts listening on all available interfaces and associated addresses, on port 8080. -Procedure: -* Copy **mini-http** executable to target directory: `cp mini-http /path/to/target/directory/` -* Start **mini-http**: `/path/to/target/directory/mini-http` -* Open browser i.e. http://localhost:8080/index.html - -The port and the address can be provided as command line arguments: -> mini-http 192.168.1.23 8090 -> -> mini-http 8090 192.168.1.23 +Arguments can be provided in any order: +> http-mini /path/to/target/directory 192.168.1.23 8090 Features: * directory listing diff --git a/src/errors/missing_source_directory.rs b/src/errors/missing_source_directory.rs new file mode 100644 index 0000000..7e46ded --- /dev/null +++ b/src/errors/missing_source_directory.rs @@ -0,0 +1,23 @@ +use std::fmt; + +const ERR_MS: &str = "Critical: missing absolute path to HTTP server source directory"; + +pub struct MissingSourceDirectoryError; + +impl fmt::Display for MissingSourceDirectoryError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", ERR_MS) + } +} + +impl fmt::Debug for MissingSourceDirectoryError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{{ message: {}, file: {}, line: {} }}", + ERR_MS, + file!(), + line!() + ) + } +} diff --git a/src/errors/mod.rs b/src/errors/mod.rs new file mode 100644 index 0000000..8ef8d35 --- /dev/null +++ b/src/errors/mod.rs @@ -0,0 +1 @@ +pub mod missing_source_directory; diff --git a/src/lib.rs b/src/lib.rs index 02f3b8d..4770dec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,46 +1,30 @@ +mod errors; mod traits; -mod utils; +pub mod utils; -use crate::utils::{app, fs}; +use crate::utils::app; use std::env; use std::process::exit; use utils::http_server; -/// # Start mini-http server -/// -/// Example: +/// # Using http-mini library: /// /// **File main.rs** /// ``` -/// use std::io::Error; -/// use std::net::TcpListener; -/// use std::process::exit; -/// use std::thread; -/// use std::thread::sleep; -/// use std::time::Duration; -/// /// extern crate http_mini_lib; /// /// fn main() { -/// println!("Starting server..."); -/// let t = thread::spawn(move || { -/// http_mini_lib::start(); -/// }); -/// -/// sleep(Duration::new(1, 0)); -/// drop(t); +/// http_mini_lib::start(); /// } /// ``` -/// -/// **shell**: -/// > rustc --extern mini_http=src/external/libmini_http_lib.rlib ./src/main.rs -/// -/// > main // grcov-excl-start pub fn start() { - let binding = fs::get_app_dir().unwrap(); - let app_dir = binding.as_path(); - let (address, port) = app::get_params(); + let get_params_result = app::get_params(); + if get_params_result.is_err() { + println!("{}", get_params_result.err().unwrap()); + exit(1); + } + let (address, port, source_dir) = get_params_result.unwrap(); let executable_path = env::current_exe().unwrap(); let executable_name = executable_path.file_name().unwrap(); @@ -67,10 +51,33 @@ pub fn start() { port ); let link_addr = llv_link_addr.as_str(); + let source_dir_path = source_dir.as_path(); for stream in listener.incoming() { let stream = stream.unwrap(); - http_server::handle_connection(stream, app_dir, executable_name, link_addr); + http_server::handle_connection(stream, source_dir_path, executable_name, link_addr); } } // grcov-excl-stop + +#[cfg(test)] +mod tests { + use crate::start; + use std::thread; + use std::thread::sleep; + use std::time::Duration; + + #[test] + fn test_start() { + let no_panic = true; + + let t = thread::spawn(move || { + start(); + }); + + sleep(Duration::new(1, 0)); + drop(t); + + assert_eq!(no_panic, true); + } +} diff --git a/src/main.rs b/src/main.rs index b1b1bf3..2f6bb46 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,68 +1,19 @@ -mod traits; -mod utils; - -use crate::utils::{app, fs}; -use std::env; -use std::process::exit; -use utils::http_server; - -/// # Start mini-http server +/// # Start http-mini server /// -/// A simple single threaded server which can be used during development, or for light traffic applications. +/// A simple HTTP server which can be used during development, or for light traffic applications. /// -/// The application will only serve content which is located in the same directory as own executable. -/// This includes any content in subdirectories. +/// The application will only serve content in a target directory which must be specified at startup. /// /// By default, the application starts listening on all available interfaces and associated addresses, on port 8080. /// -/// Procedure: -/// * Copy **mini-http** executable to target directory: `cp mini-http /path/to/target/directory/` -/// * Start **mini-http**: `/path/to/target/directory/mini-http` -/// * Open browser i.e. http:///localhost:8080/index.html -/// -/// The port and the address can be provided as command line arguments: -/// > mini-http 192.168.1.23 8090 -/// > -/// > mini-http 8090 192.168.1.23 +/// Arguments can be provided in any order: +/// > http-mini /path/to/target/directory 192.168.1.23 8090 /// /// Features: /// * directory listing /// * content type detection // grcov-excl-start fn main() { - let binding = fs::get_app_dir().unwrap(); - let app_dir = binding.as_path(); - let (address, port) = app::get_params(); - - let executable_path = env::current_exe().unwrap(); - let executable_name = executable_path.file_name().unwrap(); - if executable_name.is_empty() { - println!("Invalid executable"); - exit(1); - } - - let result = http_server::run(address.as_str(), port); - if result.is_err() { - println!("Http Server Error: {:?}", result.unwrap()); - exit(1); - } - - let listener = result.unwrap(); - - let llv_link_addr: String = format!( - "http://{}:{}", - if address == "::" { - "localhost" - } else { - address.as_str() - }, - port - ); - let link_addr = llv_link_addr.as_str(); - - for stream in listener.incoming() { - let stream = stream.unwrap(); - http_server::handle_connection(stream, app_dir, executable_name, link_addr); - } + http_mini_lib::start(); } // grcov-excl-stop diff --git a/src/traits.rs b/src/traits/mod.rs similarity index 100% rename from src/traits.rs rename to src/traits/mod.rs diff --git a/src/utils/app.rs b/src/utils/app.rs index f7fa422..6a58456 100644 --- a/src/utils/app.rs +++ b/src/utils/app.rs @@ -1,20 +1,48 @@ -use std::env; +use crate::errors::missing_source_directory::MissingSourceDirectoryError; + +use std::convert::Infallible; use std::net::{AddrParseError, IpAddr}; use std::num::ParseIntError; +use std::path::PathBuf; +use std::{env, fs}; -/// # Get port and IP address from command line arguments +/// # Get source directory, port and IP address from command line arguments +/// +/// Retrieve command line arguments which can be used as source directory, +/// startup port and IP address. /// -/// Retrieve command line arguments which can be used as startup -/// port and IP address. +/// Source directory is mandatory. /// /// Defaults: /// * IP address: "::" /// * port : 8080 -pub fn get_params() -> (String, i32) { +pub fn get_params() -> Result<(String, i32, PathBuf), MissingSourceDirectoryError> { let mut address: Option = None; let mut port: Option = None; + let mut source_dir: Option = None; + + let mut first_argument = true; for argument in env::args() { + if first_argument { + first_argument = false; + continue; + } + if argument.starts_with("-") { + continue; + } + + if source_dir.is_none() { + let x_source_dir: Result = argument.parse(); + if x_source_dir.is_ok() { + let x_source_dir_pathbuf: PathBuf = x_source_dir.unwrap(); + let canonical = fs::canonicalize(x_source_dir_pathbuf); + if canonical.is_ok() { + source_dir = Option::from(canonical.unwrap()); // grcov-excl-line + continue; // grcov-excl-line + } + } + } if port.is_none() { let x_port: Result = argument.parse(); if x_port.is_ok() { @@ -31,17 +59,18 @@ pub fn get_params() -> (String, i32) { } } - (address.unwrap_or("::".to_string()), port.unwrap_or(8080)) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_get_params() { - let (address, port) = get_params(); - assert_eq!(address.as_str(), "::"); - assert_eq!(port, 8080); + // source directory is mandatory when not testing + if source_dir.is_none() { + if env::var_os("TEST").is_some() || cfg!(test) { + source_dir = Option::from(PathBuf::from("./")); + } else { + return Err(MissingSourceDirectoryError); // grcov-excl-line + } } + + Ok(( + address.unwrap_or("::".to_string()), + port.unwrap_or(8080), + source_dir.unwrap(), + )) } diff --git a/src/utils/fs.rs b/src/utils/fs.rs index 7b18708..3326fe8 100644 --- a/src/utils/fs.rs +++ b/src/utils/fs.rs @@ -1,6 +1,6 @@ +use std::fs; use std::io::{Error, ErrorKind}; -use std::path::{Path, PathBuf}; -use std::{env, fs}; +use std::path::Path; /// # Validate path /// @@ -11,35 +11,19 @@ pub fn validate_path(path: &Path) -> bool { !path.is_symlink() && !path.is_relative() } -/// # Retrieve file contents as String -pub fn get_file_contents(path: &str) -> Result { +/// # Retrieve file contents +pub fn get_file_contents(path: &str) -> Result, Error> { let path_metadata = fs::metadata(path); if path_metadata.is_err() { return Err(path_metadata.err().unwrap()); } - let file_data = fs::read_to_string(path); + let file_data = fs::read(path); if file_data.is_err() { return Err(file_data.err().unwrap()); } - Ok(file_data.ok().unwrap().to_string()) -} - -/// # Determine application's own parent directory -pub fn get_app_dir() -> Result { - let current_exe: Result = env::current_exe(); - if current_exe.is_err() { - return Ok(PathBuf::from("./")); // grcov-excl-line - } - - let binding = current_exe.unwrap(); - let current_parent = binding.parent(); - if current_parent.is_none() { - return Ok(PathBuf::from("./")); // grcov-excl-line - } - - Ok(PathBuf::from(current_parent.unwrap().to_str().unwrap())) + Ok(file_data.ok().unwrap()) } const HTML_TEMPLATE: &str = @@ -57,14 +41,14 @@ const LISTING_DIR_SLASH: &str = "/"; pub fn get_dir_contents_as_html( path: &Path, - app_dir: &Path, + source_dir: &Path, address: &str, ) -> Result { if !path.is_dir() { return Err(Error::new(ErrorKind::NotFound, "Not a directory")); } - if !path.starts_with(app_dir) { + if !path.starts_with(source_dir) { return Err(Error::new(ErrorKind::NotFound, "Access forbidden")); } @@ -88,7 +72,7 @@ pub fn get_dir_contents_as_html( .into_os_string() .into_string() .unwrap() - .replace(app_dir.to_str().unwrap(), "."); + .replace(source_dir.to_str().unwrap(), "."); let entry_server_uri = format!("{}{}", address, entry_display_path.trim_start_matches(".")); result = format!( @@ -111,22 +95,10 @@ pub fn get_dir_contents_as_html( "###TITLE###", path.to_str() .unwrap() - .replace(app_dir.to_str().unwrap(), ".") + .replace(source_dir.to_str().unwrap(), ".") .as_str(), ) .replace("###BODY###", result.as_str()); Ok(result) } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_get_app_dir() { - let binding = get_app_dir().unwrap(); - let app_dir = binding.as_path(); - assert_eq!(app_dir, env::current_exe().unwrap().parent().unwrap()); - } -} diff --git a/src/utils/http_response.rs b/src/utils/http_response.rs index aeb4445..7394148 100644 --- a/src/utils/http_response.rs +++ b/src/utils/http_response.rs @@ -19,14 +19,15 @@ pub fn send( mut stream: &TcpStream, status_line: &str, headers: Option>, - contents_option: Option, + contents_option: Option>, ) { - let contents = contents_option.unwrap_or("".to_string()); + let contents = contents_option.unwrap_or(Vec::from("")); let content_length = contents.len(); let headers_content = build_headers(headers.unwrap_or_default(), content_length); - let response = format!("{status_line}{CRLF}{headers_content}{CRLF}{contents}"); + let response = format!("{status_line}{CRLF}{headers_content}{CRLF}"); stream.write_all(response.as_bytes()).unwrap(); + stream.write_all(&contents).unwrap(); stream.flush().unwrap(); } diff --git a/src/utils/http_server.rs b/src/utils/http_server.rs index 4a81f26..c311124 100644 --- a/src/utils/http_server.rs +++ b/src/utils/http_server.rs @@ -30,7 +30,7 @@ pub fn run(address: &str, mut port: i32) -> Result { /// # Main connections handler pub fn handle_connection( stream: TcpStream, - app_dir: &Path, + source_dir: &Path, executable_name: &OsStr, address: &str, ) { @@ -40,7 +40,7 @@ pub fn handle_connection( &stream, "HTTP/1.1 400 Bad Request", None, - Option::from(http_request.err().unwrap().to_string()), + Option::from(Vec::from(http_request.err().unwrap().to_string())), ); return; } @@ -54,11 +54,11 @@ pub fn handle_connection( return; } - let file_path = app_dir.join(request_path.unwrap().trim_start_matches('/')); + let file_path = source_dir.join(request_path.unwrap().trim_start_matches('/')); // list directory contents with usable links if file_path.is_dir() { - let dir_contents_as_html = get_dir_contents_as_html(&file_path, app_dir, address); + let dir_contents_as_html = get_dir_contents_as_html(&file_path, source_dir, address); if dir_contents_as_html.is_err() { http_response::send(&stream, "HTTP/1.1 500 Internal Server Error", None, None); return; @@ -68,7 +68,7 @@ pub fn handle_connection( &stream, "HTTP/1.1 200 OK", Option::from(vec![("Content-Type".to_string(), "text/html".to_string())]), - dir_contents_as_html.ok(), + Option::from(Vec::from(dir_contents_as_html.ok().unwrap())), ); return; } @@ -84,12 +84,12 @@ pub fn handle_connection( &stream, "HTTP/1.1 400 Bad Request", None, - Option::from(file_contents.err().unwrap().to_string()), + Option::from(Vec::from(file_contents.err().unwrap().to_string())), ); return; } - // must not allow to call own executable name i.e. http://localhost:8080/mini-http !!! + // Extra protection. Prevent calling own executable i.e. http://localhost:8080/mini-http !!! if file_path.file_name().is_none() || file_path.file_name().unwrap() == executable_name { http_response::send(&stream, "HTTP/1.1 400 Bad Request", None, None); return; diff --git a/tasks/CI b/tasks/CI index 0b5aa27..8b93917 100755 --- a/tasks/CI +++ b/tasks/CI @@ -1,16 +1,36 @@ #!/bin/sh -echo "** Checking format ..." -cargo fmt --check +printf "\n** Checking format ...\n" +if cargo fmt --check ; then + : +else + printf "\033[1;31m%s\033[0m\n" "**! Format error(s)" +fi -echo "** Checking code ..." -cargo clippy --no-deps +printf "\n** Checking code ...\n" +if cargo clippy --no-deps ; then + : +else + printf "\033[1;31m%s\033[0m\n" "**! Coding mistake(s)" +fi -echo "** Verifying project ..." -cargo verify-project +printf "\n** Verifying project ...\n" +if cargo verify-project ; then + : +else + printf "\033[1;31m%s\033[0m\n" "**! Crate manifest error(s)" +fi -echo "** Building ..." -cargo build +printf "\n** Building ...\n" +if cargo build ; then + : +else + printf "\033[1;31m%s\033[0m\n" "**! Build failed" +fi -echo "** Testing ..." -cargo test --no-fail-fast +printf "\n** Testing ...\n" +if cargo test --no-fail-fast --all-targets -- ./ ; then + : +else + printf "\033[1;31m%s\033[0m\n" "**! Test error(s)" +fi diff --git a/tasks/test b/tasks/test index dca0262..b4543ec 100755 --- a/tasks/test +++ b/tasks/test @@ -9,10 +9,10 @@ export RUSTFLAGS LLVM_PROFILE_FILE='generated/coverage/data/cargo-test-%p-%m.profraw' export LLVM_PROFILE_FILE -cargo test --no-fail-fast +env TEST=1 cargo test --no-fail-fast -- --nocapture 8080 "::" grcov . --binary-path ./target/debug/deps/ -s . -t html \ --branch --ignore-not-existing --ignore '../*' --ignore "/*" \ --excl-line "grcov-excl-line|^$|#\[|mod |use |^\\W*\/\/|^\\W*\}" \ --excl-start "grcov-excl-start" --excl-stop "grcov-excl-stop" \ - -o ./generated/coverage/html/ + -o ./generated/coverage/