Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CON-3318 Fix rust piscine's quest-04 #149

Merged
merged 12 commits into from
Mar 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
221 changes: 26 additions & 195 deletions solutions/banner/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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<String, ParseFloatError> {
let first_number = a.parse::<f32>()?;
let second_number = b.parse::<f32>()?;
Ok((first_number / second_number).to_string())
}
#[allow(dead_code)]
pub fn rem(a: &str, b: &str) -> Result<String, ParseFloatError> {
let first_number = a.parse::<f32>()?;
let second_number = b.parse::<f32>()?;
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<String, ParseFloatError>;

#[allow(dead_code)]
pub struct FlagsHandler {
pub flags: HashMap<(String, String), Callback>,
pub flags: HashMap<String, Callback>,
}

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<String, String> {
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<String, ParseFloatError> {
Ok((a.parse::<f64>()? / b.parse::<f64>()?).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<String, ParseFloatError> {
Ok((a.parse::<f64>()? % b.parse::<f64>()?).to_string())
}
3 changes: 0 additions & 3 deletions solutions/boxing_todo/malformed_object.json

This file was deleted.

29 changes: 14 additions & 15 deletions solutions/boxing_todo/src/err.rs
Original file line number Diff line number Diff line change
@@ -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<dyn Error>),
Empty,
Malformed(Box<dyn Error>),
}

// 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")
}
}

Expand All @@ -20,24 +20,23 @@ pub struct ReadErr {
pub child_err: Box<dyn Error>,
}

// 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")
}
}

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())
}
}
65 changes: 21 additions & 44 deletions solutions/boxing_todo/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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<Task>,
}

impl TodoList {
pub fn new(title: String, tasks: Vec<Task>) -> TodoList {
TodoList { title, tasks }
}
pub fn get_todo(path: &str) -> Result<TodoList, Box<dyn Error>> {
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<String, Box<dyn Error>> {
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<TodoList, Box<dyn Error>> {
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)
}
7 changes: 0 additions & 7 deletions solutions/boxing_todo/todo.json

This file was deleted.

Loading
Loading