diff --git a/solutions/banner/src/lib.rs b/solutions/banner/src/lib.rs index ae91621c..adc2d3ca 100644 --- a/solutions/banner/src/lib.rs +++ b/solutions/banner/src/lib.rs @@ -1,213 +1,44 @@ -/* -## banner +use std::{collections::HashMap, num::ParseFloatError}; -### Instructions - -`Result` is a better version of the `Option` type that describes possible error instead -of possible absence - -Create a structure called `Flag` that as the following elements: - - - short_hand: String - - long_hand: String - - desc: String - -This structure must have associated to it a function called `opt_flag` that initializes the structure. -Receiving two references strings and returns the structure `Flag`. It should be used like this: - -```rust - let d = Flag::opt_flag("diff", "gives the difference between two numbers"); - - println!("short hand: {}, long hand: {}, description: {}", d.short_hand, d.long_hand, d.desc); - // output: "short hand: -d, long hand: --diff, description: gives the difference between two numbers" -``` - -It will be given a second structure called `FlagsHandler` that has just one element: `flags: HashMap<(String, String), Callback>` -And the following functions associated to it, for you to complete : - - - `add_flag`, that adds to the HashMap the flag and the Callback function. - - `exec_func`, that executes the function using the flag provided and returns the result, that can - be either a string with the value from the callback or an error. - -It will also be provided a `type` called `Callback` being a function that is going to be used in the structure -and functions above. This function will be the callback for the flag associated to it. - -You will have to create the following callback functions : - - - `div`, that converts the reference strings to `float`s and returns the `Result`, being the division of the `float`s - or the standard (std) error: `ParseFloatError`. - - `rem`, that converts the reference strings to `float`s and returns the `Result`, being the remainder of the division - of the `float`s or the standard (std) error `ParseFloatError`. - -### Example - -```rust -fn main() { - let mut handler = FlagsHandler { flags: HashMap::new() }; - - let d = Flag::opt_flag("division", "divides the values, formula (a / b)"); - let r = Flag::opt_flag( - "remainder", - "remainder of the division between two values, formula (a % b)", - ); - - handler.add_flag((d.short_hand, d.long_hand), div); - handler.add_flag((r.short_hand, r.long_hand), rem); - - println!("{:?}", handler.exec_func(("-d".to_string(), "--division".to_string()), &["1.0", "2.0"])); - // output: "0.5" - - println!("{:?}",handler.exec_func(("-r".to_string(), "--remainder".to_string()), &["2.0", "2.0"])); - // output: "0.0" - - println!("{:?}",handler.exec_func(("-d".to_string(), "--division".to_string()), &["a", "2.0"])); - // output: "invalid float literal" - - println!("{:?}",handler.exec_func(("-r".to_string(), "--remainder".to_string()), &["2.0", "fd"])); - // output: "invalid float literal" +#[derive(PartialEq, Eq, Hash)] +pub struct Flag<'a> { + pub short_hand: String, + pub long_hand: String, + pub desc: &'a str, } -``` - -### Notions - -- https://doc.rust-lang.org/rust-by-example/error/result.html -- https://docs.rs/getopts/0.2.18/getopts/struct.Options.html#method.optflag -*/ -use std::collections::HashMap; -use std::num::ParseFloatError; - -#[allow(dead_code)] -pub fn div(a: &str, b: &str) -> Result { - let first_number = a.parse::()?; - let second_number = b.parse::()?; - Ok((first_number / second_number).to_string()) -} -#[allow(dead_code)] -pub fn rem(a: &str, b: &str) -> Result { - let first_number = a.parse::()?; - let second_number = b.parse::()?; - Ok((first_number % second_number).to_string()) +impl<'a> Flag<'a> { + pub fn opt_flag(name: &'a str, d: &'a str) -> Self { + Self { + short_hand: format!("-{}", name.chars().next().unwrap()), + long_hand: format!("--{}", name), + desc: d, + } + } } -#[allow(dead_code)] pub type Callback = fn(&str, &str) -> Result; -#[allow(dead_code)] pub struct FlagsHandler { - pub flags: HashMap<(String, String), Callback>, + pub flags: HashMap, } impl FlagsHandler { - #[allow(dead_code)] - pub fn add_flag(&mut self, flag: (String, String), func: Callback) { - self.flags.insert(flag, func); + pub fn add_flag(&mut self, flag: Flag, func: Callback) { + self.flags.insert(flag.short_hand, func); + self.flags.insert(flag.long_hand, func); } - #[allow(dead_code)] - pub fn exec_func(&mut self, flag: (String, String), argv: &[&str]) -> String { - match self.flags[&flag](argv[0], argv[1]) { - Ok(res) => res, - Err(e) => e.to_string(), - } - } -} -#[derive(Debug)] -pub struct Flag { - pub short_hand: String, - pub long_hand: String, - pub desc: String, -} - -impl Flag { - #[allow(dead_code)] - pub fn opt_flag(l_h: &str, d: &str) -> Flag { - let mut a = "-".to_string(); - a.push_str(&l_h[0..1]); - Flag { - short_hand: a, - long_hand: "--".to_string() + &l_h.to_string(), - desc: d.to_string(), - } + pub fn exec_func(&self, input: &str, argv: &[&str]) -> Result { + let f = self.flags[input]; + f(argv[0], argv[1]).map_err(|e| e.to_string()) } } -#[cfg(test)] -mod tests { - use super::*; - - fn init() -> FlagsHandler { - let d = Flag::opt_flag("division", "divides two numbers"); - let r = Flag::opt_flag( - "remainder", - "gives the remainder of the division between two numbers", - ); - let mut handler = FlagsHandler { - flags: HashMap::new(), - }; - - handler.add_flag((d.short_hand, d.long_hand), div); - handler.add_flag((r.short_hand, r.long_hand), rem); - return handler; - } - - #[test] - fn ok_test() { - let mut handler = init(); - assert_eq!( - handler.exec_func( - ("-d".to_string(), "--division".to_string()), - &["1.0", "2.0"] - ), - "0.5" - ); - assert_eq!( - handler.exec_func( - ("-r".to_string(), "--remainder".to_string()), - &["2.0", "2.0"] - ), - "0" - ); - assert_eq!( - handler.exec_func( - ("-d".to_string(), "--division".to_string()), - &["12.323", "212.32"] - ), - "0.05803975" - ); - assert_eq!( - handler.exec_func( - ("-r".to_string(), "--remainder".to_string()), - &["12.323", "212.32"] - ), - "12.323" - ); - } +pub fn div(a: &str, b: &str) -> Result { + Ok((a.parse::()? / b.parse::()?).to_string()) +} - #[test] - fn error_test() { - let mut handler = init(); - assert_eq!( - handler.exec_func(("-d".to_string(), "--division".to_string()), &["a", "2.0"]), - "invalid float literal" - ); - assert_eq!( - handler.exec_func(("-r".to_string(), "--remainder".to_string()), &["2.0", "f"]), - "invalid float literal" - ); - assert_eq!( - handler.exec_func( - ("-d".to_string(), "--division".to_string()), - &["1.0", "0.0"] - ), - "inf" - ); - assert_eq!( - handler.exec_func( - ("-r".to_string(), "--remainder".to_string()), - &["2.0", "0.0"] - ), - "NaN" - ); - } +pub fn rem(a: &str, b: &str) -> Result { + Ok((a.parse::()? % b.parse::()?).to_string()) } diff --git a/solutions/boxing_todo/malformed_object.json b/solutions/boxing_todo/malformed_object.json deleted file mode 100644 index e1651765..00000000 --- a/solutions/boxing_todo/malformed_object.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "something": , -} \ No newline at end of file diff --git a/solutions/boxing_todo/src/err.rs b/solutions/boxing_todo/src/err.rs index fe8e1a40..a3c46fb0 100644 --- a/solutions/boxing_todo/src/err.rs +++ b/solutions/boxing_todo/src/err.rs @@ -1,17 +1,17 @@ -use std::error::Error; -use std::fmt; -use std::fmt::Display; +use std::{ + error::Error, + fmt::{self, Display}, +}; #[derive(Debug)] pub enum ParseErr { - Malformed(Box), Empty, + Malformed(Box), } -// required by error trait impl Display for ParseErr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Fail to parse todo") + write!(f, "Failed to parse todo file") } } @@ -20,16 +20,9 @@ pub struct ReadErr { pub child_err: Box, } -// required by error trait impl Display for ReadErr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Fail to read todo file") - } -} - -impl Error for ReadErr { - fn source(&self) -> Option<&(dyn Error + 'static)> { - Some(&*self.child_err) + write!(f, "Failed to read todo file") } } @@ -37,7 +30,13 @@ impl Error for ParseErr { fn source(&self) -> Option<&(dyn Error + 'static)> { match &self { ParseErr::Empty => None, - _ => Some(&*self), + _ => Some(self), } } } + +impl Error for ReadErr { + fn source(&self) -> Option<&(dyn Error + 'static)> { + Some(self.child_err.as_ref()) + } +} diff --git a/solutions/boxing_todo/src/lib.rs b/solutions/boxing_todo/src/lib.rs index 3b6599d1..7c00cf95 100644 --- a/solutions/boxing_todo/src/lib.rs +++ b/solutions/boxing_todo/src/lib.rs @@ -1,9 +1,8 @@ mod err; pub use err::{ParseErr, ReadErr}; -pub use std::error::Error; -use json::{parse, stringify}; -use std::fs::read_to_string; +use std::error::Error; +use std::fs; #[derive(Debug, Eq, PartialEq)] pub struct Task { @@ -12,55 +11,33 @@ pub struct Task { pub level: u32, } -impl Task { - pub fn new(id: u32, description: String, level: u32) -> Task { - Task { - id, - description, - level, - } - } -} - #[derive(Debug, Eq, PartialEq)] pub struct TodoList { pub title: String, pub tasks: Vec, } + impl TodoList { - pub fn new(title: String, tasks: Vec) -> TodoList { - TodoList { title, tasks } - } pub fn get_todo(path: &str) -> Result> { - let todo_raw = read_todos(path); - let parsed_todos = parse_todos(&todo_raw?)?; - Ok(parsed_todos) - } -} + let contents = fs::read_to_string(path).map_err(|e| ReadErr { + child_err: Box::new(e), + })?; -pub fn read_todos(path: &str) -> Result> { - let raw = read_to_string(path).map_err(|e| ReadErr { - child_err: Box::new(e), - })?; - Ok(raw) -} + let contents = json::parse(&contents).map_err(|e| ParseErr::Malformed(Box::new(e)))?; + if contents["tasks"].is_empty() { + return Err(ParseErr::Empty.into()); + } -pub fn parse_todos(todo_str: &str) -> Result> { - let parset = parse(todo_str).map_err(|e| ParseErr::Malformed(Box::new(e)))?; - if parset["tasks"].is_empty() { - return Err(ParseErr::Empty.into()); - } - let mut v = vec![]; - for i in 0..parset["tasks"].len() { - let a = &parset["tasks"][i]; - let task = stringify(a["description"].clone()); - v.push(Task::new( - stringify(a["id"].clone()).parse().unwrap(), - task.get(1..task.len() - 1).unwrap().to_string(), - stringify(a["level"].clone()).parse().unwrap(), - )); + Ok(Self { + title: contents["title"].as_str().unwrap().to_owned(), + tasks: contents["tasks"] + .members() + .map(|m| Task { + id: m["id"].as_u32().unwrap(), + description: m["description"].as_str().unwrap().to_owned(), + level: m["level"].as_u32().unwrap(), + }) + .collect(), + }) } - let title = stringify(parset["title"].clone()); - let todo_list = TodoList::new(title.get(1..title.len() - 1).unwrap().to_string(), v); - Ok(todo_list) } diff --git a/solutions/boxing_todo/todo.json b/solutions/boxing_todo/todo.json deleted file mode 100644 index 2341ac78..00000000 --- a/solutions/boxing_todo/todo.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "title" : "TODO LIST FOR PISCINE RUST", - "tasks": [ - { "id": 0, "description": "do this", "level": 0 }, - { "id": 1, "description": "do that", "level": 5 } - ] -} \ No newline at end of file diff --git a/solutions/boxing_todo/todo_empty.json b/solutions/boxing_todo/todo_empty.json deleted file mode 100644 index 9dfc54f5..00000000 --- a/solutions/boxing_todo/todo_empty.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title" : "TODO LIST FOR PISCINE RUST", - "tasks": [] -} \ No newline at end of file diff --git a/solutions/cipher/src/lib.rs b/solutions/cipher/src/lib.rs index d49783e0..85291155 100644 --- a/solutions/cipher/src/lib.rs +++ b/solutions/cipher/src/lib.rs @@ -1,45 +1,30 @@ -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, PartialEq)] pub struct CipherError { - pub validation: bool, pub expected: String, } -impl CipherError { - pub fn new(validation: bool, expected: String) -> CipherError { - CipherError { - validation, - expected, - } - } -} +pub fn cipher(original: &str, ciphered: &str) -> Result<(), CipherError> { + let decoded = decode(original); -pub fn cipher(original: &str, ciphered: &str) -> Option> { - if ciphered == "" || original == "" { - return None; - } - match decode(original) == ciphered { - true => Some(Ok(true)), - false => Some(Err(CipherError::new( - decode(original) == ciphered, - decode(original), - ))), + if decoded == ciphered { + Ok(()) + } else { + Err(CipherError { expected: decoded }) } } +const CHARS: u8 = 'z' as u8 - 'a' as u8; + fn decode(original: &str) -> String { original .chars() - .map(|letter| match letter.is_ascii_alphabetic() { - true => { - match letter.is_uppercase() { - true => (((25 - (letter as u32 - 'A' as u32)) + 'A' as u32) as u8 as char) - .to_string(), - false => (((25 - (letter as u32 - 'a' as u32)) + 'a' as u32) as u8 as char) - .to_string(), - } + .map(|l| { + if l.is_ascii_alphabetic() { + let offset = (if l.is_ascii_uppercase() { 'A' } else { 'a' }) as u8; + ((CHARS - (l as u8 - offset)) + offset) as char + } else { + l } - false => letter.to_string(), }) - .collect::>() - .join("") + .collect() } diff --git a/solutions/error_types/src/lib.rs b/solutions/error_types/src/lib.rs index 13571eb2..6eabea40 100644 --- a/solutions/error_types/src/lib.rs +++ b/solutions/error_types/src/lib.rs @@ -1,15 +1,15 @@ -pub use chrono::{NaiveDate, Utc}; +pub use chrono::Utc; #[derive(Debug, Eq, PartialEq)] pub struct FormError { - pub form_values: (String, String), + pub form_values: (&'static str, String), pub date: String, - pub err: String, + pub err: &'static str, } impl FormError { - pub fn new(field_name: String, field_value: String, err: String) -> FormError { - FormError { + pub fn new(field_name: &'static str, field_value: String, err: &'static str) -> Self { + Self { form_values: (field_name, field_value), date: Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(), err, @@ -19,73 +19,34 @@ impl FormError { #[derive(Debug, Eq, PartialEq)] pub struct Form { - pub first_name: String, - pub last_name: String, - pub birth: NaiveDate, - pub birth_location: String, + pub name: String, pub password: String, } impl Form { - pub fn new( - first_name: String, - last_name: String, - birth: NaiveDate, - birth_location: String, - password: String, - ) -> Form { - Form { - first_name, - last_name, - birth, - birth_location, - password, + pub fn validate(&self) -> Result<(), FormError> { + if self.name.is_empty() { + return Err(FormError::new( + "name", + self.name.clone(), + "Username is empty", + )); } - } - - pub fn validate(&self) -> Result, FormError> { - let v: Vec<&str> = vec![ - match &self.first_name { - x if x == &String::from("") => Err(self.user_name()), - _ => Ok("Valid first name"), - }?, - match &self.password { - x if x.len() < 8 => Err(self.has_eight_char()), - _ if !self.validate_ascii_char() => Err(self.has_diff_ascii_char()), - _ => Ok("Valid password"), - }?, - ]; - Ok(v) - } - fn user_name(&self) -> FormError { - FormError::new( - String::from("first_name"), - self.first_name.clone(), - String::from("No user name"), - ) - } - - // all this is the part to validate all the parts of the form - fn has_eight_char(&self) -> FormError { - FormError::new( - String::from("password"), - self.password.clone(), - String::from("At least 8 characters"), - ) - } - - fn has_diff_ascii_char(&self) -> FormError { - FormError::new(String::from("password"), self.password.clone(), String::from("Combination of different ASCII character types (numbers, letters and none alphanumeric characters)")) - } + let password_error = + |m: &'static str| Err(FormError::new("password", self.password.clone(), m)); + + if self.password.len() < 8 { + return password_error("Password should be at least 8 characters long"); + } else if !(self.password.chars().any(|c| c.is_ascii_digit()) + && self.password.chars().any(|c| c.is_ascii_alphabetic()) + && self.password.chars().any(|c| c.is_ascii_punctuation())) + { + return password_error( + "Password should be a combination of ASCII numbers, letters and symbols", + ); + } - fn validate_ascii_char(&self) -> bool { - let chars: Vec = self.password.chars().collect(); - chars.iter().any(|c| c.is_digit(10)) && chars.iter().any(|c| !c.is_alphanumeric()) + Ok(()) } } - -#[allow(dead_code)] -fn create_date(date: &str) -> NaiveDate { - NaiveDate::parse_from_str(date, "%Y-%m-%d").unwrap() -} diff --git a/solutions/handling/src/lib.rs b/solutions/handling/src/lib.rs index 10f37aaa..09e73aa9 100644 --- a/solutions/handling/src/lib.rs +++ b/solutions/handling/src/lib.rs @@ -1,97 +1,11 @@ -/* -## handling +use std::{fs::OpenOptions, io::Write, path::Path}; -### Instructions +pub fn open_or_create>(path: &P, content: &str) { + let mut f = OpenOptions::new() + .append(true) + .create(true) + .open(path) + .unwrap(); -Write a function, called `open_or_create` that as two arguments: -- `file : &str` which is the name of the files -- `content: &str` being the content to be written into the file - -This functions should try to open a file, if it does not exist creates it. -You should panic, with the error, in case something goes wrong. - -### Example - -```rust -fn main() { - let path = "a.txt"; - File::create(path).unwrap(); - open_or_create(path, "content to be written"); - - let mut file = File::open(path).unwrap(); - - let mut s = String::new(); - file.read_to_string(&mut s).unwrap(); - println!("{}", s); - // output: content to be written -} -``` - -### Notions - -- https://doc.rust-lang.org/std/io/enum.ErrorKind.html -- https://doc.rust-lang.org/std/fs/struct.File.html -*/ - -use std::fs::{File, OpenOptions}; -use std::io::{ErrorKind, Write}; - -pub fn open_or_create(s: &str, content: &str) { - let mut f = match OpenOptions::new().write(true).open(s) { - Ok(file) => file, - Err(ref error) if error.kind() == ErrorKind::NotFound => match File::create(s) { - Ok(fc) => fc, - Err(e) => panic!("{:?}", e), - }, - Err(error) => panic!("{:?}", error), - }; - f.write_all(content.as_bytes()).unwrap(); -} - -#[cfg(test)] -mod tests { - use super::*; - use std::fs; - use std::panic; - - use std::io::prelude::*; - - fn get_file_content(filename: &str) -> String { - let mut file = File::open(filename).unwrap(); - let mut s = String::new(); - file.read_to_string(&mut s).unwrap(); - fs::remove_file(filename).unwrap(); - return s; - } - - #[test] - fn test_if_file_exists() { - let filename = "test_existing_file.txt"; - let content = "hello world!"; - File::create(filename).unwrap(); - open_or_create(filename, content); - - assert_eq!(content, get_file_content(filename)); - } - - #[test] - fn test_create_file() { - let file = "no_existing_file.txt"; - let content = "hello world!"; - open_or_create(file, content); - - assert_eq!(content, get_file_content(file)); - } - #[test] - fn test_error_case() { - let filename = "hello.txt"; - File::create(filename).unwrap(); - let mut perms = fs::metadata(filename).unwrap().permissions(); - perms.set_readonly(true); - fs::set_permissions(filename, perms).unwrap(); - - let result = panic::catch_unwind(|| open_or_create(filename, "test")); - fs::remove_file(filename).unwrap(); - assert!(result.is_err()); - } + f.write(content.as_bytes()).unwrap(); } diff --git a/solutions/panic/src/lib.rs b/solutions/panic/src/lib.rs index a9ac22e1..e3fc5fec 100644 --- a/solutions/panic/src/lib.rs +++ b/solutions/panic/src/lib.rs @@ -1,40 +1,5 @@ -/* -## error types - -### Instructions - -Write a function that tries to open a file and panics if the file -doesn't exist - -*/ - use std::fs::File; pub fn open_file(s: &str) -> File { - let file = File::open(s); - if let Ok(f) = file { - return f; - } else { - panic!("File not found") - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::fs; - - #[test] - #[should_panic(expected = "File not found")] - fn test_opening() { - open_file("file.txt"); - } - - #[test] - fn test_opening_existing() { - let filename = "created.txt"; - File::create(filename).unwrap(); - open_file(filename); - fs::remove_file(filename).unwrap(); - } + File::open(s).unwrap() } diff --git a/solutions/profanity_filter/src/lib.rs b/solutions/profanity_filter/src/lib.rs index 20a785d8..d1d124d7 100644 --- a/solutions/profanity_filter/src/lib.rs +++ b/solutions/profanity_filter/src/lib.rs @@ -1,64 +1,9 @@ -#[allow(dead_code)] -pub struct Message { - pub content: String, - pub user: String, -} - -#[allow(dead_code)] -impl Message { - pub fn new(ms: String, u: String) -> Message { - Message { - content: ms, - user: u, - } - } - // allows the student to use options - #[allow(dead_code)] - pub fn send_ms(&self) -> Option<&str> { - match (&self.content[..], "stupid") { - (x, p) if x.contains(p) || x == "" => None, - _ => Some(&self.content), - } - } -} - -// the student can catch the None and return it as an error -#[allow(dead_code)] -pub fn check_ms(ms: &Message) -> (bool, &str) { - match ms.send_ms() { - Some(e) => (true, e), - None => (false, "ERROR: illegal"), - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_error_ms() { - let v = vec![ - Message::new("".to_string(), "toby".to_string()), - Message::new("stupid".to_string(), "jack".to_string()), - Message::new("you are stupid".to_string(), "jacob".to_string()), - ]; - for value in v { - let (t, _) = check_ms(&value); - assert!(!t); - } - } +const WORD: &str = "stupid"; - #[test] - fn test_ok_ms() { - let v = vec![ - Message::new("get out of the car".to_string(), "police".to_string()), - Message::new("no!".to_string(), "thief".to_string()), - Message::new("get the werewolf".to_string(), "police".to_string()), - Message::new("wait the wha...".to_string(), "thief".to_string()), - ]; - for value in v { - let (t, _) = check_ms(&value); - assert!(t); - } +pub fn check_ms(message: &str) -> Result<&str, &str> { + if message.is_empty() || message.contains(WORD) { + Err("ERROR: illegal") + } else { + Ok(message) } } diff --git a/solutions/question_mark/src/lib.rs b/solutions/question_mark/src/lib.rs index 4f7aabc3..25b7a9db 100644 --- a/solutions/question_mark/src/lib.rs +++ b/solutions/question_mark/src/lib.rs @@ -1,45 +1,3 @@ -/* -## question_mark - -### Instructions - -You will have to create 3 structures: - -- `One`, that contains one element called `first_layer` it should be an `Option` for the structure `Two`. -- `Two`, that contains one element called `second_layer` it should be an `Option` for the structure `Three`. -- `Three`, that contains one element called `third_layer` it should be an `Option` for the structure `Four`. -- `Four`, that contains one element called `fourth_layer` it should be an `u16` that is an `Option`. - -Beside the structure you must create a function named `get_fourth_layer` that is associated to the `One` structure. -This function should return the `Option` value in the `Four` structure. - -### Example - -```rust -fn main() { - let a = One { - first_layer : Some(Two { - second_layer: Some(Three { - third_layer: Some(Four { - fourth_layer: Some(1000) - }) - }) - }) - }; - - // output: 1000 - println!("{:?}", match a.get_fourth_layer() { - Some(e) => e, - None => 0 - }) -} -``` - -### Notions - -- https://doc.rust-lang.org/stable/rust-by-example/error/option_unwrap/question_mark.html - -*/ #[derive(Clone, Copy)] pub struct One { pub first_layer: Option, @@ -61,35 +19,7 @@ pub struct Four { } impl One { - pub fn get_fourth_layer(&self) -> Option { + pub fn get_fourth_layer(self) -> Option { self.first_layer?.second_layer?.third_layer?.fourth_layer } } - -#[cfg(test)] -mod tests { - use super::*; - #[test] - fn test_value() { - let a = One { - first_layer: Some(Two { - second_layer: Some(Three { - third_layer: Some(Four { - fourth_layer: Some(1000), - }), - }), - }), - }; - let b = One { - first_layer: Some(Two { - second_layer: Some(Three { - third_layer: Some(Four { - fourth_layer: Some(3), - }), - }), - }), - }; - assert_eq!(a.get_fourth_layer(), Some(1000)); - assert_eq!(b.get_fourth_layer(), Some(3)); - } -} diff --git a/solutions/unwrap_or_expect/src/lib.rs b/solutions/unwrap_or_expect/src/lib.rs index 93575c4c..b6cb0d58 100644 --- a/solutions/unwrap_or_expect/src/lib.rs +++ b/solutions/unwrap_or_expect/src/lib.rs @@ -1,17 +1,19 @@ pub enum Security { Unknown, - High, - Medium, - Low, - BlockServer, + Message, + Warning, + NotFound, + UnexpectedUrl, } -pub fn fetch_data(server: Result, security_level: Security) -> String { +pub fn fetch_data(server: Result<&str, &str>, security_level: Security) -> String { match security_level { - Security::Unknown => server.unwrap(), - Security::High => server.expect("ERROR: program stops"), - Security::Medium => server.unwrap_or("WARNING: check the server".to_string()), - Security::Low => server.unwrap_or_else(|url| "Not found: ".to_string() + url.as_str()), - Security::BlockServer => server.unwrap_err(), + Security::Unknown => server.unwrap().to_owned(), + Security::Message => server.expect("ERROR: program stops").to_owned(), + Security::Warning => server.unwrap_or("WARNING: check the server").to_owned(), + Security::NotFound => server + .map(String::from) + .unwrap_or_else(|url| format!("Not found: {}", url)), + Security::UnexpectedUrl => server.unwrap_err().to_owned(), } } diff --git a/tests/banner_test/src/lib.rs b/tests/banner_test/src/lib.rs new file mode 100644 index 00000000..8e2500cc --- /dev/null +++ b/tests/banner_test/src/lib.rs @@ -0,0 +1,69 @@ +use banner::*; +use std::{collections::HashMap, sync::LazyLock}; + +static HANDLER: LazyLock = LazyLock::new(|| { + let mut handler = FlagsHandler { + flags: HashMap::new(), + }; + + handler.add_flag(Flag::opt_flag("division", "divides two numbers"), div); + handler.add_flag( + Flag::opt_flag( + "remainder", + "gives the remainder of the division between two numbers", + ), + rem, + ); + + handler +}); + +#[test] +fn test_simple() { + for a in ["-d", "--division"] { + assert_eq!(HANDLER.exec_func(a, &["1.0", "2.0"]), Ok("0.5".to_owned())); + } + + for a in ["-r", "--remainder"] { + assert_eq!(HANDLER.exec_func(a, &["2.0", "2.0"]), Ok("0".to_owned())); + } + + for a in ["-d", "--division"] { + assert_eq!( + HANDLER.exec_func(a, &["12.323", "212.32"]), + Ok("0.058039751318764134".to_owned()) + ); + } + + for a in ["-r", "--remainder"] { + assert_eq!( + HANDLER.exec_func(a, &["12.323", "212.32"]), + Ok("12.323".to_owned()) + ); + } +} + +#[test] +fn test_edge_cases() { + for a in ["-d", "--division"] { + assert_eq!( + HANDLER.exec_func(a, &["a", "2.0"]), + Err("invalid float literal".to_owned()) + ); + } + + for a in ["-r", "--remainder"] { + assert_eq!( + HANDLER.exec_func(a, &["2.0", "f"]), + Err("invalid float literal".to_owned()) + ); + } + + for a in ["-d", "--division"] { + assert_eq!(HANDLER.exec_func(a, &["1.0", "0.0"]), Ok("inf".to_owned())); + } + + for a in ["-r", "--remainder"] { + assert_eq!(HANDLER.exec_func(a, &["1.0", "0.0"]), Ok("NaN".to_owned())); + } +} diff --git a/tests/banner_test/src/main.rs b/tests/banner_test/src/main.rs deleted file mode 100644 index 9525bde6..00000000 --- a/tests/banner_test/src/main.rs +++ /dev/null @@ -1,130 +0,0 @@ -use banner::*; -use std::collections::HashMap; - -fn main() { - let mut handler = FlagsHandler { - flags: HashMap::new(), - }; - - let d = Flag::opt_flag("division", "divides the values, formula (a / b)"); - let r = Flag::opt_flag( - "remainder", - "remainder of the division between two values, formula (a % b)", - ); - - handler.add_flag((d.short_hand, d.long_hand), div); - handler.add_flag((r.short_hand, r.long_hand), rem); - - println!( - "{:?}", - handler.exec_func( - ("-d".to_string(), "--division".to_string()), - &["1.0", "2.0"] - ) - ); - // output: "0.5" - - println!( - "{:?}", - handler.exec_func( - ("-r".to_string(), "--remainder".to_string()), - &["2.0", "2.0"] - ) - ); - // output: "0.0" - - println!( - "{:?}", - handler.exec_func(("-d".to_string(), "--division".to_string()), &["a", "2.0"]) - ); - // output: "invalid float literal" - - println!( - "{:?}", - handler.exec_func( - ("-r".to_string(), "--remainder".to_string()), - &["2.0", "fd"] - ) - ); - // output: "invalid float literal" -} - -#[cfg(test)] -mod tests { - use super::*; - - fn init() -> FlagsHandler { - let d = Flag::opt_flag("division", "divides two numbers"); - let r = Flag::opt_flag( - "remainder", - "gives the remainder of the division between two numbers", - ); - let mut handler = FlagsHandler { - flags: HashMap::new(), - }; - - handler.add_flag((d.short_hand, d.long_hand), div); - handler.add_flag((r.short_hand, r.long_hand), rem); - return handler; - } - - #[test] - fn ok_test() { - let mut handler = init(); - assert_eq!( - handler.exec_func( - ("-d".to_string(), "--division".to_string()), - &["1.0", "2.0"] - ), - "0.5" - ); - assert_eq!( - handler.exec_func( - ("-r".to_string(), "--remainder".to_string()), - &["2.0", "2.0"] - ), - "0" - ); - assert_eq!( - handler.exec_func( - ("-d".to_string(), "--division".to_string()), - &["12.323", "212.32"] - ), - "0.05803975" - ); - assert_eq!( - handler.exec_func( - ("-r".to_string(), "--remainder".to_string()), - &["12.323", "212.32"] - ), - "12.323" - ); - } - - #[test] - fn error_test() { - let mut handler = init(); - assert_eq!( - handler.exec_func(("-d".to_string(), "--division".to_string()), &["a", "2.0"]), - "invalid float literal" - ); - assert_eq!( - handler.exec_func(("-r".to_string(), "--remainder".to_string()), &["2.0", "f"]), - "invalid float literal" - ); - assert_eq!( - handler.exec_func( - ("-d".to_string(), "--division".to_string()), - &["1.0", "0.0"] - ), - "inf" - ); - assert_eq!( - handler.exec_func( - ("-r".to_string(), "--remainder".to_string()), - &["2.0", "0.0"] - ), - "NaN" - ); - } -} diff --git a/tests/boxing_todo_test/Cargo.toml b/tests/boxing_todo_test/Cargo.toml index 40e3bdce..dbb967b9 100644 --- a/tests/boxing_todo_test/Cargo.toml +++ b/tests/boxing_todo_test/Cargo.toml @@ -9,3 +9,4 @@ edition = "2021" [dependencies] boxing_todo = { path = "../../solutions/boxing_todo"} json="0.12.4" +tempfile = "3.17.1" diff --git a/tests/boxing_todo_test/src/lib.rs b/tests/boxing_todo_test/src/lib.rs new file mode 100644 index 00000000..e4b17704 --- /dev/null +++ b/tests/boxing_todo_test/src/lib.rs @@ -0,0 +1,89 @@ +use boxing_todo::*; + +use json::{object, JsonValue}; +use std::error::Error; +use std::io::{self, Write}; +use tempfile::NamedTempFile; + +fn write_and_read_test( + write_f: impl FnOnce(&mut NamedTempFile), +) -> Result> { + let mut file = NamedTempFile::new().unwrap(); + write_f(&mut file); + + let path = file.path().to_str().unwrap(); + TodoList::get_todo(path) +} + +#[inline] +fn json_to_file_to_mem(obj: JsonValue) -> Result> { + write_and_read_test(|f| obj.write(f).unwrap()) +} + +#[test] +fn test_valid_todo() { + let r#struct = TodoList { + title: "todo list for something".to_owned(), + tasks: vec![ + Task { + id: 0, + description: "do this".to_owned(), + level: 0, + }, + Task { + id: 1, + description: "do that".to_owned(), + level: 5, + }, + ], + }; + + let obj = object! { + "title" : "todo list for something", + "tasks": [ + { "id": 0, "description": "do this", "level": 0 }, + { "id": 1, "description": "do that", "level": 5 } + ] + }; + + assert_eq!(r#struct, json_to_file_to_mem(obj).unwrap()); +} + +#[test] +fn test_empty_tasks() { + let obj = object! { + "title" : "empty tasks", + "tasks": [] + }; + + let result = json_to_file_to_mem(obj).unwrap_err(); + + assert!(matches!(result.downcast_ref().unwrap(), ParseErr::Empty)); + assert!(result.source().is_none()); + assert_eq!(result.to_string(), "Failed to parse todo file"); +} + +#[test] +fn test_read_err() { + let result = TodoList::get_todo("invalid_file.json").unwrap_err(); + + let ReadErr { child_err } = result.downcast_ref().unwrap(); + + assert!(child_err.is::()); + assert!(result.source().unwrap().is::()); + assert_eq!(result.to_string(), "Failed to read todo file"); +} + +#[test] +fn test_parse_err_malformed() { + let result = write_and_read_test(|f| f.write_all(r#"{"something": ,}"#.as_bytes()).unwrap()) + .unwrap_err(); + + let ParseErr::Malformed(e) = result.downcast_ref::().unwrap() else { + panic!() + }; + + assert!(e.is::()); + assert!(result.source().unwrap().is::()); + assert_eq!(result.to_string(), "Failed to parse todo file"); +} diff --git a/tests/boxing_todo_test/src/main.rs b/tests/boxing_todo_test/src/main.rs deleted file mode 100644 index 48ae8f07..00000000 --- a/tests/boxing_todo_test/src/main.rs +++ /dev/null @@ -1,127 +0,0 @@ -use boxing_todo::*; - -// Note that you can create some todo list your self to test it, but you can find the JSON files that -// are being tested [here](https://github.com/01-edu/public/blob/master/subjects/boxing_todo) -fn main() { - let todos = TodoList::get_todo("todo.json"); - match todos { - Ok(list) => println!("{:?}", list), - Err(e) => { - println!("{} {:?}", e.to_string(), e.source()); - } - } - - let todos = TodoList::get_todo("malformed_object.json"); - match todos { - Ok(list) => println!("{:?}", list), - Err(e) => { - println!("{} {:?}", e.to_string(), e.source()); - } - } - - let todos = TodoList::get_todo("permission_err.json"); - match todos { - Ok(list) => println!("{:?}", list), - Err(e) => { - println!("{} {:?}", e.to_string(), e.source()); - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use json::{object, JsonValue}; - use std::fs; - use std::fs::{File, OpenOptions}; - use std::io::Write; - - fn new_todo(s: String, v: Vec) -> TodoList { - TodoList { title: s, tasks: v } - } - - fn run(s: JsonValue, f: &str) -> Result> { - File::create(f)?; - let mut file = OpenOptions::new().append(true).open(f)?; - file.write_all(s.dump().as_bytes())?; - let result = TodoList::get_todo(f); - fs::remove_file(f)?; - return result; - } - - #[test] - fn test_good_todo() { - let file_name = "todo.json"; - let good_struct = new_todo( - String::from("todo list for something"), - vec![ - Task { - id: 0, - description: "do this".to_string(), - level: 0, - }, - Task { - id: 1, - description: "do that".to_string(), - level: 5, - }, - ], - ); - let obj = object! { - "title" : "todo list for something", - "tasks": [ - { "id": 0, "description": "do this", "level": 0 }, - { "id": 1, "description": "do that", "level": 5 } - ] - }; - - let result = run(obj, file_name).unwrap(); - - assert_eq!(result.title, good_struct.title); - assert_eq!(&result.tasks, &good_struct.tasks); - } - - #[test] - fn test_empty_tasks() { - let result = run( - object! { - "title" : "empty tasks", - "tasks": []}, - "empty_tasks.json", - ) - .unwrap_err(); - - assert_eq!(result.to_string(), "Fail to parse todo"); - assert!(result.source().is_none()); - } - - #[test] - fn test_read() { - let result = TodoList::get_todo("no_file.json").unwrap_err(); - assert_eq!(result.to_string(), "Fail to read todo file"); - } - - #[test] - #[should_panic( - expected = "Fail to read todo file Some(Os { code: 2, kind: NotFound, message: \"No such file or directory\" })" - )] - fn test_read_error() { - let result = TodoList::get_todo("no_file.json"); - result.unwrap_or_else(|e| panic!("{} {:?}", e.to_string(), e.source())); - } - - #[test] - #[should_panic( - expected = "Fail to parse todo Some(Malformed(UnexpectedCharacter { ch: \',\', line: 1, column: 15 }))" - )] - fn test_malformed_error() { - let file_name = "malformed.json"; - File::create(file_name).unwrap(); - let mut file = OpenOptions::new().append(true).open(file_name).unwrap(); - file.write_all(r#"{"something": ,}"#.as_bytes()).unwrap(); - let result = TodoList::get_todo(file_name); - fs::remove_file(file_name).unwrap(); - - result.unwrap_or_else(|e| panic!("{} {:?}", e.to_string(), e.source())); - } -} diff --git a/tests/cipher_test/src/lib.rs b/tests/cipher_test/src/lib.rs new file mode 100644 index 00000000..639a2e93 --- /dev/null +++ b/tests/cipher_test/src/lib.rs @@ -0,0 +1,47 @@ +use cipher::*; + +#[test] +fn test_ok_values() { + assert_eq!(cipher("1Hello 2world!", "1Svool 2dliow!"), Ok(())); + assert_eq!(cipher("asdasd", "zhwzhw"), Ok(())); + assert_eq!(cipher("3(/&%fsd 32das", "3(/&%uhw 32wzh"), Ok(())); +} + +#[test] +fn test_empty_values() { + assert_eq!(cipher("", ""), Ok(())); + assert_eq!( + cipher("", "1Svool 2dliow!"), + Err(CipherError { + expected: "".to_owned() + }) + ); + assert_eq!( + cipher("1Hello 2world!", ""), + Err(CipherError { + expected: "1Svool 2dliow!".to_owned() + }) + ); +} + +#[test] +fn test_errors() { + assert_eq!( + cipher("1Hello 2world!", "1svool 2dliow!"), + Err(CipherError { + expected: String::from("1Svool 2dliow!") + }) + ); + assert_eq!( + cipher("asdasd", "lkdas"), + Err(CipherError { + expected: String::from("zhwzhw") + }) + ); + assert_eq!( + cipher("3(/&%sd 32das", "3(/&%uhw 32wzh"), + Err(CipherError { + expected: String::from("3(/&%hw 32wzh") + }) + ); +} diff --git a/tests/cipher_test/src/main.rs b/tests/cipher_test/src/main.rs deleted file mode 100644 index 4691bc85..00000000 --- a/tests/cipher_test/src/main.rs +++ /dev/null @@ -1,50 +0,0 @@ -use cipher::*; - -fn main() { - println!("{:?}", cipher("1Hello 2world!", "1Svool 2dliow!")); - println!("{:?}", cipher("1Hello 2world!", "svool")); - println!("{:?}", cipher("", "svool")); -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_ok_values() { - assert_eq!(cipher("1Hello 2world!", "1Svool 2dliow!"), Some(Ok(true))); - assert_eq!(cipher("asdasd", "zhwzhw"), Some(Ok(true))); - assert_eq!(cipher("3(/&%fsd 32das", "3(/&%uhw 32wzh"), Some(Ok(true))); - } - - #[test] - fn test_empty_values() { - assert_eq!(cipher("", "1Svool 2dliow!"), None); - assert_eq!(cipher("1Hello 2world!", ""), None); - } - - #[test] - fn test_errors() { - assert_eq!( - cipher("1Hello 2world!", "1svool 2dliow!"), - Some(Err(CipherError { - validation: false, - expected: String::from("1Svool 2dliow!") - })) - ); - assert_eq!( - cipher("asdasd", "lkdas"), - Some(Err(CipherError { - validation: false, - expected: String::from("zhwzhw") - })) - ); - assert_eq!( - cipher("3(/&%sd 32das", "3(/&%uhw 32wzh"), - Some(Err(CipherError { - validation: false, - expected: String::from("3(/&%hw 32wzh") - })) - ); - } -} diff --git a/tests/error_types_test/src/lib.rs b/tests/error_types_test/src/lib.rs new file mode 100644 index 00000000..b9329aea --- /dev/null +++ b/tests/error_types_test/src/lib.rs @@ -0,0 +1,73 @@ +use error_types::*; + +#[test] +fn test_error_type() { + let cases = [ + ( + Form { + name: "Katy".to_owned(), + password: "qwTw12&%$3sa1dty_".to_owned(), + }, + Ok(()), + ), + ( + Form { + name: "".to_owned(), + password: "qwTw12&%$3sa1dty_".to_owned(), + }, + Err(FormError { + form_values: ("name", "".to_owned()), + date: Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(), + err: "Username is empty", + }), + ), + ( + Form { + name: "Someone".to_owned(), + password: "12345".to_owned(), + }, + Err(FormError { + form_values: ("password", "12345".to_owned()), + date: Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(), + err: "Password should be at least 8 characters long", + }), + ), + ( + Form { + name: "Someone".to_owned(), + password: "sdASDsrW".to_owned(), + }, + Err(FormError { + form_values: ("password", "sdASDsrW".to_owned()), + date: Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(), + err: "Password should be a combination of ASCII numbers, letters and symbols", + }), + ), + ( + Form { + name: "Someone".to_owned(), + password: "dsGE1SAD213".to_owned(), + }, + Err(FormError { + form_values: ("password", "dsGE1SAD213".to_owned()), + date: Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(), + err: "Password should be a combination of ASCII numbers, letters and symbols", + }), + ), + ( + Form { + name: "Someone".to_owned(), + password: "dsaSD&%DF!?=".to_owned(), + }, + Err(FormError { + form_values: ("password", String::from("dsaSD&%DF!?=")), + date: Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(), + err: "Password should be a combination of ASCII numbers, letters and symbols", + }), + ), + ]; + + for (form, expected) in cases { + assert_eq!(form.validate(), expected); + } +} diff --git a/tests/error_types_test/src/main.rs b/tests/error_types_test/src/main.rs deleted file mode 100644 index 84d066e3..00000000 --- a/tests/error_types_test/src/main.rs +++ /dev/null @@ -1,135 +0,0 @@ -use error_types::*; - -#[allow(dead_code)] -fn create_date(date: &str) -> NaiveDate { - NaiveDate::parse_from_str(date, "%Y-%m-%d").unwrap() -} - -fn main() { - let mut form_output = Form::new( - String::from("Lee"), - String::from("Silva"), - create_date("2015-09-05"), - String::from("Africa"), - String::from("qwqwsa1dty_"), - ); - - println!("{:?}", form_output); - println!("{:?}", form_output.validate().unwrap()); - - form_output.first_name = String::from(""); - println!("{:?}", form_output.validate().unwrap_err()); - - form_output.first_name = String::from("as"); - form_output.password = String::from("dty_1"); - println!("{:?}", form_output.validate().unwrap_err()); - - form_output.password = String::from("asdasASd(_"); - println!("{:?}", form_output.validate().unwrap_err()); - - form_output.password = String::from("asdasASd123SA"); - println!("{:?}", form_output.validate().unwrap_err()); -} - -#[cfg(test)] -mod tests { - use super::*; - - #[derive(Debug)] - struct TestForm<'a> { - form: Form, - validation: Result, FormError>, - } - - impl<'a> TestForm<'_> { - // all test cases - fn new() -> Vec> { - vec![ - TestForm { - form : Form::new( - String::from("Katy"), - String::from("Silva"), - create_date("2015-09-05"), - String::from("Africa"), - String::from("qwTw12&%$3sa1dty_")), - validation: Ok(vec!["Valid first name", "Valid password"]), - }, - TestForm { - form : Form::new( - String::from(""), - String::from("Bear"), - create_date("2015-09-05"), - String::from("Africa"), - String::from("qwTw12&%$3sa1dty_")), - validation: Err(FormError { - form_values: (String::from("first_name"), - String::from("")), - date: Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(), - err: String::from("No user name")}), - }, - TestForm { - form : Form::new( - String::from("Someone"), - String::from("Bear"), - create_date("2015-09-05"), - String::from("Africa"), - String::from("12345")), - validation: Err(FormError { - form_values: (String::from("password"), String::from("12345")), - date: Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(), - err: String::from("At least 8 characters") }), - }, - TestForm { - form : Form::new( - String::from("Someone"), - String::from("Bear"), - create_date("2015-09-05"), - String::from("Africa"), - String::from("sdASDsrW")), - validation: Err(FormError { - form_values: (String::from("password"), String::from("sdASDsrW")), - date: Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(), - err: String::from("Combination of different ASCII character types (numbers, letters and none alphanumeric characters)") }), - }, - TestForm { - form : Form::new( - String::from("Someone"), - String::from("Bear"), - create_date("2015-09-05"), - String::from("Africa"), - String::from("dsGE1SAD213")), - validation: Err(FormError { - form_values: (String::from("password"), String::from("dsGE1SAD213")), - date: Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(), - err: String::from("Combination of different ASCII character types (numbers, letters and none alphanumeric characters)") }), - }, - TestForm { - form : Form::new( - String::from("Someone"), - String::from("Bear"), - create_date("2015-09-05"), - String::from("Africa"), - String::from("dsaSD&%DF!?=")), - validation: Err(FormError { - form_values: (String::from("password"), String::from("dsaSD&%DF!?=")), - date: Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(), - err: String::from("Combination of different ASCII character types (numbers, letters and none alphanumeric characters)") }), - } - ] - } - } - - #[test] - fn test_error_type() { - let form_cases = TestForm::new(); - - for v in form_cases { - assert_eq!( - v.form.validate(), - v.validation, - "Tested with {:?}", - v.validation.as_ref().err().unwrap().form_values - ); - } - } -} diff --git a/tests/handling_test/Cargo.toml b/tests/handling_test/Cargo.toml index c8e2cbfb..9d256b4b 100644 --- a/tests/handling_test/Cargo.toml +++ b/tests/handling_test/Cargo.toml @@ -8,3 +8,4 @@ edition = "2021" [dependencies] handling = { path = "../../solutions/handling"} +tempfile = "3.16.0" diff --git a/tests/handling_test/a.txt b/tests/handling_test/a.txt deleted file mode 100644 index 7dde7fad..00000000 --- a/tests/handling_test/a.txt +++ /dev/null @@ -1 +0,0 @@ -content to be written \ No newline at end of file diff --git a/tests/handling_test/src/lib.rs b/tests/handling_test/src/lib.rs new file mode 100644 index 00000000..ed3633a1 --- /dev/null +++ b/tests/handling_test/src/lib.rs @@ -0,0 +1,53 @@ +use std::{fs, io::Write}; +use tempfile::NamedTempFile; + +#[test] +fn test_with_non_existing_file() { + let file = NamedTempFile::new().unwrap(); + let path = file.path().to_path_buf(); + file.close().unwrap(); + let content = "hello world!"; + + handling::open_or_create(&path, content); + + assert_eq!(content, fs::read_to_string(path).unwrap()); +} + +#[test] +fn test_with_empty_file() { + let path = NamedTempFile::new().unwrap().into_temp_path(); + let content = "hello world!"; + + handling::open_or_create(&path, content); + + assert_eq!(content, fs::read_to_string(path).unwrap()); +} + +#[test] +fn test_with_file() { + let mut file = NamedTempFile::new().unwrap(); + let initial_content = "some content\n"; + file.write_all(initial_content.as_bytes()).unwrap(); + let path = file.into_temp_path(); + let content = "hello world!"; + + handling::open_or_create(&path, content); + + assert_eq!( + format!("{}{}", initial_content, content), + fs::read_to_string(path).unwrap() + ); +} + +#[test] +#[should_panic] +fn test_with_file_with_insufficient_permissions() { + let file = NamedTempFile::new().unwrap(); + let mut permissions = file.as_file().metadata().unwrap().permissions(); + permissions.set_readonly(true); + file.as_file().set_permissions(permissions).unwrap(); + let path = file.into_temp_path(); + let content = "hello world!"; + + handling::open_or_create(&path, content); +} diff --git a/tests/handling_test/src/main.rs b/tests/handling_test/src/main.rs deleted file mode 100644 index f3f77561..00000000 --- a/tests/handling_test/src/main.rs +++ /dev/null @@ -1,63 +0,0 @@ -// -use std::io::prelude::*; -// -use handling::*; -use std::fs::File; - -fn main() { - let path = "a.txt"; - File::create(path).unwrap(); - open_or_create(path, "content to be written"); - - let mut file = File::open(path).unwrap(); - - let mut s = String::new(); - file.read_to_string(&mut s).unwrap(); - println!("{}", s); - // output: content to be written -} - -#[cfg(test)] -mod tests { - use super::*; - use std::fs; - use std::panic; - fn get_file_content(filename: &str) -> String { - let mut file = File::open(filename).unwrap(); - let mut s = String::new(); - file.read_to_string(&mut s).unwrap(); - fs::remove_file(filename).unwrap(); - return s; - } - - #[test] - fn test_if_file_exists() { - let filename = "test_existing_file.txt"; - let content = "hello world!"; - File::create(filename).unwrap(); - open_or_create(filename, content); - - assert_eq!(content, get_file_content(filename)); - } - - #[test] - fn test_create_file() { - let file = "no_existing_file.txt"; - let content = "hello world!"; - open_or_create(file, content); - - assert_eq!(content, get_file_content(file)); - } - #[test] - fn test_error_case() { - let filename = "hello.txt"; - File::create(filename).unwrap(); - let mut perms = fs::metadata(filename).unwrap().permissions(); - perms.set_readonly(true); - fs::set_permissions(filename, perms).unwrap(); - - let result = panic::catch_unwind(|| open_or_create(filename, "test")); - fs::remove_file(filename).unwrap(); - assert!(result.is_err()); - } -} diff --git a/tests/panic_test/src/lib.rs b/tests/panic_test/src/lib.rs new file mode 100644 index 00000000..f250526f --- /dev/null +++ b/tests/panic_test/src/lib.rs @@ -0,0 +1,16 @@ +use panic::*; +use std::fs::{self, File}; + +#[test] +#[should_panic(expected = "No such file or directory")] +fn test_opening() { + open_file("file.txt"); +} + +#[test] +fn test_opening_existing() { + let filename = "created.txt"; + File::create(filename).unwrap(); + open_file(filename); + fs::remove_file(filename).unwrap(); +} diff --git a/tests/panic_test/src/main.rs b/tests/panic_test/src/main.rs deleted file mode 100644 index 85075faf..00000000 --- a/tests/panic_test/src/main.rs +++ /dev/null @@ -1,29 +0,0 @@ -use panic::*; -use std::fs::{self, File}; - -fn main() { - let filename = "created.txt"; - File::create(filename).unwrap(); - let a = open_file(filename); - println!("{:?}", a); - fs::remove_file(filename).unwrap(); -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - #[should_panic(expected = "File not found")] - fn test_opening() { - open_file("file.txt"); - } - - #[test] - fn test_opening_existing() { - let filename = "created.txt"; - File::create(filename).unwrap(); - open_file(filename); - fs::remove_file(filename).unwrap(); - } -} diff --git a/tests/profanity_filter_test/src/lib.rs b/tests/profanity_filter_test/src/lib.rs new file mode 100644 index 00000000..cd71813e --- /dev/null +++ b/tests/profanity_filter_test/src/lib.rs @@ -0,0 +1,18 @@ +#[test] +fn test_error_ms() { + ["", "stupid", "you are stupid"] + .into_iter() + .for_each(|m| assert_eq!(Err("ERROR: illegal"), profanity_filter::check_ms(m))); +} + +#[test] +fn test_ok_ms() { + [ + "get out of the car", + "no!", + "get the werewolf", + "wait the what...", + ] + .into_iter() + .for_each(|m| assert_eq!(Ok(m), profanity_filter::check_ms(m))); +} diff --git a/tests/profanity_filter_test/src/main.rs b/tests/profanity_filter_test/src/main.rs deleted file mode 100644 index b56b520d..00000000 --- a/tests/profanity_filter_test/src/main.rs +++ /dev/null @@ -1,51 +0,0 @@ -use profanity_filter::*; - -fn main() { - let m0 = Message::new("hello there".to_string(), "toby".to_string()); - println!("{:?}", check_ms(&m0)); - // output: (true, "hello there") - - let m1 = Message::new("".to_string(), "toby".to_string()); - println!("{:?}", check_ms(&m1)); - // output: (false, "ERROR: illegal") - - let m2 = Message::new("you are stupid".to_string(), "toby".to_string()); - println!("{:?}", check_ms(&m2)); - // output: (false, "ERROR: illegal") - - let m3 = Message::new("stupid".to_string(), "toby".to_string()); - println!("{:?}", check_ms(&m3)); - // output: (false, "ERROR: illegal") -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_error_ms() { - let v = vec![ - Message::new("".to_string(), "toby".to_string()), - Message::new("stupid".to_string(), "jack".to_string()), - Message::new("you are stupid".to_string(), "jacob".to_string()), - ]; - for value in v { - let (t, _) = check_ms(&value); - assert!(!t); - } - } - - #[test] - fn test_ok_ms() { - let v = vec![ - Message::new("get out of the car".to_string(), "police".to_string()), - Message::new("no!".to_string(), "thief".to_string()), - Message::new("get the werewolf".to_string(), "police".to_string()), - Message::new("wait the wha...".to_string(), "thief".to_string()), - ]; - for value in v { - let (t, _) = check_ms(&value); - assert!(t); - } - } -} diff --git a/tests/question_mark_test/src/lib.rs b/tests/question_mark_test/src/lib.rs new file mode 100644 index 00000000..b2c8474d --- /dev/null +++ b/tests/question_mark_test/src/lib.rs @@ -0,0 +1,19 @@ +use question_mark::*; + +fn create_nested(value: Option) -> One { + One { + first_layer: Some(Two { + second_layer: Some(Three { + third_layer: Some(Four { + fourth_layer: value, + }), + }), + }), + } +} + +#[test] +fn test_value() { + assert_eq!(create_nested(Some(1000)).get_fourth_layer(), Some(1000)); + assert_eq!(create_nested(None).get_fourth_layer(), None); +} diff --git a/tests/question_mark_test/src/main.rs b/tests/question_mark_test/src/main.rs deleted file mode 100644 index ad5f6ea0..00000000 --- a/tests/question_mark_test/src/main.rs +++ /dev/null @@ -1,50 +0,0 @@ -use question_mark::*; - -fn main() { - let a = One { - first_layer: Some(Two { - second_layer: Some(Three { - third_layer: Some(Four { - fourth_layer: Some(1000), - }), - }), - }), - }; - - // output: 1000 - println!( - "{:?}", - match a.get_fourth_layer() { - Some(e) => e, - None => 0, - } - ) -} - -#[cfg(test)] -mod tests { - use super::*; - #[test] - fn test_value() { - let a = One { - first_layer: Some(Two { - second_layer: Some(Three { - third_layer: Some(Four { - fourth_layer: Some(1000), - }), - }), - }), - }; - let b = One { - first_layer: Some(Two { - second_layer: Some(Three { - third_layer: Some(Four { - fourth_layer: Some(3), - }), - }), - }), - }; - assert_eq!(a.get_fourth_layer(), Some(1000)); - assert_eq!(b.get_fourth_layer(), Some(3)); - } -} diff --git a/tests/unwrap_or_expect_test/src/main.rs b/tests/unwrap_or_expect_test/src/main.rs index 7efc7f67..75900e9f 100644 --- a/tests/unwrap_or_expect_test/src/main.rs +++ b/tests/unwrap_or_expect_test/src/main.rs @@ -1,6 +1,19 @@ use unwrap_or_expect::*; -fn main() {} +fn main() { + println!("{}", fetch_data(Ok("server1.com"), Security::Warning)); + println!("{}", fetch_data(Err("server.com"), Security::Warning)); + println!("{}", fetch_data(Err("server2.com"), Security::NotFound)); + + // Panics with no custom message + // fetch_data(Err("ERROR CRITICAL"), Security::Unknown); + + // Panics with the message "ERROR: program stops" + // fetch_data(Err("server.com"), Security::Message); + + // Panics with the message "malicious_server.com" + // fetch_data(Ok("malicious_server.com"), Security::UnexpectedUrl); +} #[cfg(test)] mod tests { @@ -9,51 +22,48 @@ mod tests { #[test] #[should_panic(expected = "ERROR: program stops")] fn test_expect() { - fetch_data(Err(String::new()), Security::High); + fetch_data(Err(""), Security::Message); } #[test] #[should_panic(expected = "called `Result::unwrap()` on an `Err` value: \"ERROR CRITICAL\"")] fn test_unwrap() { - fetch_data(Err("ERROR CRITICAL".to_string()), Security::Unknown); + fetch_data(Err("ERROR CRITICAL"), Security::Unknown); } #[test] #[should_panic(expected = "malicious_server.com")] fn test_unwrap_err() { - fetch_data( - Ok("malicious_server.com".to_string()), - Security::BlockServer, - ); + fetch_data(Ok("malicious_server.com"), Security::UnexpectedUrl); } #[test] fn test_unwrap_or() { assert_eq!( - fetch_data(Err(String::new()), Security::Medium), + fetch_data(Err(""), Security::Warning), "WARNING: check the server".to_string() ); } #[test] fn test_unwrap_or_else() { assert_eq!( - fetch_data(Err("another_server.com".to_string()), Security::Low), + fetch_data(Err("another_server.com"), Security::NotFound), "Not found: another_server.com".to_string() ); } #[test] fn test_ok() { assert_eq!( - fetch_data(Ok("server.com".to_string()), Security::Low), + fetch_data(Ok("server.com"), Security::Message), "server.com" ); assert_eq!( - fetch_data(Ok("server.com".to_string()), Security::Medium), + fetch_data(Ok("server.com"), Security::Warning), "server.com" ); assert_eq!( - fetch_data(Ok("server.com".to_string()), Security::High), + fetch_data(Ok("server.com"), Security::NotFound), "server.com" ); assert_eq!( - fetch_data(Ok("server.com".to_string()), Security::Unknown), + fetch_data(Ok("server.com"), Security::Unknown), "server.com" ); }