guessing_utils/lib.rs
1//! # Guessing utilities
2//! This crate is ideal for writing programs for guessing numbers in range (0..101).
3//! It provides a handful of utilities made for various cases.
4
5use rand::Rng;
6
7/// Used for creating a guess. This is a good alternative to storing a guessed number,
8/// which is much safer and comfortable to use compared to a primitive number.
9///
10/// # Usage
11/// Use `Guess::new(val: i32)` to create a guess object. The object will store the number now.
12/// An error might be returned, make sure to handle in appropriate situations.
13/// Now, the object can be used to compare with other guesses, extract the value, etc.
14///
15/// # Safety
16/// The constructor automatically checks if a provided input is in correct range.
17/// The object provides other utilities, like parsing which provide a safe way to do error handling.
18///
19/// # Comfort
20/// The object implements equality and comparing checks to use with other guesses,
21/// therefore not having to rely on `value()` function every time.
22#[derive(Eq, Debug)]
23pub struct Guess {
24 val: i32, // i32 instead of u32 for future capabilities
25}
26
27/// Custom-written error handling.
28pub mod err {
29 use std::fmt;
30
31 /// Used when the provided argument is outside the required (0..101) range.
32 /// Usually returned by `Guess::new(val: i32)` function when an invalid input is provided.
33 #[derive(Debug, Clone)]
34 pub struct GuessRangeError;
35
36 impl fmt::Display for GuessRangeError {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 write!(f, "The guess value was out of 0-100 range")
39 }
40 }
41
42 impl std::error::Error for GuessRangeError {}
43}
44
45impl Guess {
46 /// Creates a new `Guess` object.
47 /// An error will be returned if the provided number was outside (0..101) range.
48 /// # Example
49 /// ```
50 /// use guessing_utils::Guess;
51 ///
52 /// let value = 100; // our guess number
53 /// let guess = Guess::new(value);
54 ///
55 /// let guess = match guess {
56 /// Ok(val) => val,
57 /// Err(err) => panic!("Provided value was outside the range!"),
58 /// };
59 /// ```
60 pub fn new(val: i32) -> Result<Guess, err::GuessRangeError> {
61 if val < 0 || val > 100 {
62 return Err(err::GuessRangeError);
63 }
64
65 Ok(Guess { val })
66 }
67
68 /// Creates a new object from parsing the provided string slice.
69 /// Invalid input will produce either a parsing or a range error.
70 /// # Example
71 /// ```
72 /// use guessing_utils::Guess;
73 ///
74 /// let guess = "57"; // value to parse (input)
75 ///
76 /// let guess = match Guess::parse(&guess) {
77 /// Ok(val) => val,
78 /// Err(err) => panic!("{}", err), // handle the input error
79 /// };
80 /// ```
81 pub fn parse(val: &str) -> Result<Guess, Box<dyn std::error::Error>> {
82 let parsed: i32 = match val.trim().parse() {
83 Ok(val) => val,
84 Err(err) => return Err(err)?,
85 };
86
87 match Guess::new(parsed) {
88 Ok(val) => Ok(val),
89 Err(err) => Err(err)?
90 }
91 }
92
93 /// Gets the value stored in the object.
94 /// # Example
95 /// ```
96 /// use guessing_utils::Guess;
97 ///
98 /// let guess = Guess::new(15).unwrap();
99 /// println!("{}", guess.value()); // 15
100 /// ```
101 pub fn value(&self) -> &i32 {
102 &self.val
103 }
104}
105
106impl Ord for Guess {
107 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
108 self.value().cmp(other.value())
109 }
110}
111
112impl PartialOrd for Guess {
113 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
114 Some(self.cmp(other))
115 }
116}
117
118impl PartialEq for Guess {
119 fn eq(&self, other: &Self) -> bool {
120 self.value() == other.value()
121 }
122}
123
124/// Generates a `Guess` object together with a randomly generated number in (0..101) range put inside the object.
125/// # Example
126/// ```
127/// use guessing_utils::Guess;
128/// use guessing_utils::gen_random;
129///
130/// use std::cmp::Ordering;
131///
132/// let random_guess = gen_random();
133/// let my_guess = Guess::new(50).unwrap();
134///
135/// match random_guess.cmp(&my_guess) {
136/// Ordering::Equal => println!("I guessed the number!"),
137/// _ => (),
138/// }
139/// ```
140pub fn gen_random() -> Guess {
141 Guess::new(rand::thread_rng().gen_range(0..101)).unwrap()
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147 use std::cmp::Ordering;
148
149 #[test]
150 fn eq_test() {
151 let guess1 = Guess::new(13).unwrap();
152 let guess2 = Guess::new(13).unwrap();
153
154 assert_eq!(guess1, guess2);
155 }
156
157 #[test]
158 fn cmp_test() {
159 let guess1 = Guess::new(50).unwrap();
160 let guess2 = Guess::new(13).unwrap();
161
162 assert_eq!(guess1.cmp(&guess2), Ordering::Greater);
163 }
164
165 #[test]
166 fn parse_test() {
167 match Guess::parse("16") {
168 Ok(_) => (),
169 Err(err) => panic!("Should had no errors, got: {}", err),
170 }
171
172 match Guess::parse("val") {
173 Ok(_) => panic!("Should have panicked but didn't."),
174 Err(_) => (),
175 }
176 }
177}