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
    }
}