1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/*
# Easy Average Grade Calculator 

This tool is a easy average grade average calulcator written in Rust. It takes a list of graded to calculated the average and can be used as a library by other applications.

See a more complex [grade calulcator](https://www.thebestdegree.com/grade-calculator/) for weighted grade calculation for college or high school students and teachers. 

These tools are all implemented in Rust.

## Usage ##

The easy grade calulcator can either read the input grades from arguments or a text file as shown below:

```bash
grade-avg 3.5 2.5
```

Another way to use the tool would be to create a txt file as the input grades. Create a input file `grades.txt` with the contents:

```
1.5
3.5
2.5
```

The average for these grades can be calculcated as below:



```bash
cat ~/grades.txt | grade-avg
```
*/
use std::error::Error;
use std::fmt;
//let strict_grades: Vec<f32> = vec![1.0, 1.3, 1.7, 2.0, 2.3, 2.7, 3.0, 3.3, 3.7, 4.0, 4.3, 4.7, 5.0];

#[derive(Debug, Clone, Copy)]
pub struct Grade {
    val: usize,
}

impl fmt::Display for Grade {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let out_val = (self.val as f32) / 10.0;
        write!(f, "{:.1}", out_val)
    }
}

impl PartialEq for Grade {
    fn eq(&self, other: &Self) -> bool {
        self.val == other.val
    }
}

impl Grade {
    pub fn from(input: &str) -> Result<Grade, Box<dyn Error>> {
        let parsed_val = input.replace(",", ".").parse::<f32>()?;
        let val = (parsed_val * 10.0) as usize;
        if val >= 10 && val <= 50 {
            Ok(Grade { val })
        } else {
            Err(format!("{} is out of bounds! Must be between 1.0 and 5.0", input).into())
        }
    }

    pub fn verbal(&self) -> &str {
        if self.val >= 10 && self.val < 16 {
            "Grade A"
        } else if self.val >= 16 && self.val < 25 {
            "Grade B"
        } else if self.val >= 26 && self.val < 35 {
            "Grade C"
        } else if self.val >= 36 && self.val < 41 {
            "Grade D"
        } else if self.val >= 41 && self.val <= 50 {
            "Grade F"
        } else {
            unreachable!()
        }
    }
}

pub fn avg(grade_list: &[Grade]) -> Option<Grade> {
    if !grade_list.is_empty() {
        let acc: usize = grade_list.iter().map(|e| e.val).sum();
        let grade = (acc / grade_list.len()) as usize;
        Some(Grade { val: grade })
    } else {
        None
    }
}

pub fn avg_prec(grade_list: &[Grade]) -> Option<f64> {
    if !grade_list.is_empty() {
        let acc: usize = grade_list.iter().map(|grade| grade.val).sum();
        Some((acc as f64) / (10.0 * grade_list.len() as f64))
    } else {
        None
    }
}