88//!
99//! See the `LICENSE` file for Copyright and license details.
1010//!
11- //!
11+
12+ use rand:: { thread_rng, Rng } ;
13+ use reqwest:: blocking:: Client ;
14+ use reqwest:: header:: CONTENT_TYPE ;
15+ use serde:: { Serialize , Deserialize } ;
16+ use std:: fmt;
1217use clap:: { Parser } ;
1318
1419/// Defines the Ambi Mock Client command line interface as a struct
@@ -24,6 +29,185 @@ pub struct Cli {
2429 pub debug : bool ,
2530}
2631
32+
33+ #[ derive( Serialize , Deserialize ) ]
34+ struct Reading {
35+ temperature : String ,
36+ humidity : String ,
37+ pressure : String ,
38+ dust_concentration : String ,
39+ air_purity : String
40+ }
41+
42+ impl Reading {
43+ fn new (
44+ temperature : String ,
45+ humidity : String ,
46+ pressure : String ,
47+ dust_concentration : String ,
48+ air_purity : String
49+ ) -> Reading {
50+ Reading {
51+ temperature,
52+ humidity,
53+ pressure,
54+ dust_concentration,
55+ air_purity
56+ }
57+ }
58+ }
59+
60+ #[ derive( Debug , PartialEq ) ]
61+ enum AirPurity {
62+ Dangerous ,
63+ High ,
64+ Low ,
65+ FreshAir
66+ }
67+
68+ impl AirPurity {
69+ fn from_value ( value : u16 ) -> AirPurity {
70+ match value {
71+ 0 ..=50 => return AirPurity :: FreshAir ,
72+ 51 ..=100 => return AirPurity :: Low ,
73+ 101 ..=150 => return AirPurity :: High ,
74+ 151 .. => return AirPurity :: Dangerous ,
75+ } ;
76+ }
77+ }
78+
79+ // implements fmt::Display for AirPurity so that we can call .to_string() on
80+ // each enum value
81+ impl fmt:: Display for AirPurity {
82+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
83+ match self {
84+ AirPurity :: Low => write ! ( f, "Fresh Air" ) ,
85+ AirPurity :: High => write ! ( f, "Low Pollution" ) ,
86+ AirPurity :: Dangerous => write ! ( f, "High Pollution" ) ,
87+ AirPurity :: FreshAir => write ! ( f, "Dangerous Pollution" )
88+ }
89+ }
90+ }
91+
92+ fn random_gen_humidity ( ) -> String {
93+ let mut rng = thread_rng ( ) ;
94+ let value = rng. gen_range ( 0.0 ..=100.0 ) ;
95+ format ! ( "{:.1}" , value)
96+ }
97+
98+ fn random_gen_temperature ( ) -> String {
99+ let mut rng = thread_rng ( ) ;
100+ let value = rng. gen_range ( 15.0 ..=35.0 ) ;
101+ format ! ( "{:.1}" , value)
102+ }
103+
104+ fn random_gen_pressure ( ) -> String {
105+ let mut rng = thread_rng ( ) ;
106+ rng. gen_range ( 900 ..=1100 ) . to_string ( )
107+ }
108+
109+ fn random_gen_dust_concentration ( ) -> String {
110+ let mut rng = thread_rng ( ) ;
111+ rng. gen_range ( 0 ..=1000 ) . to_string ( )
112+ }
113+
27114pub fn run ( cli : & Cli ) {
28115 println ! ( "\r \n cli: {:?}\r \n " , cli) ;
29- }
116+
117+ let dust_concentration = random_gen_dust_concentration ( ) ;
118+ let air_purity = AirPurity :: from_value ( dust_concentration. parse :: < u16 > ( ) . unwrap ( ) ) . to_string ( ) ;
119+ let reading = Reading :: new (
120+ random_gen_temperature ( ) ,
121+ random_gen_humidity ( ) ,
122+ random_gen_pressure ( ) ,
123+ dust_concentration,
124+ air_purity
125+ ) ;
126+
127+ let json = serde_json:: to_string ( & reading) . unwrap ( ) ;
128+ const URL : & str = "http://localhost:4000/api/readings/add" ;
129+
130+ println ! ( "Sending POST request to {} as JSON: {}" , URL , json) ;
131+
132+ let client = Client :: new ( ) ;
133+ let res = client
134+ . post ( URL )
135+ . header ( CONTENT_TYPE , "application/json" )
136+ . body ( json)
137+ . send ( ) ;
138+
139+ match res {
140+ Ok ( response) => {
141+ match cli. debug {
142+ true => println ! ( "Response from Ambi backend: {:#?}" , response) ,
143+ false => println ! ( "Response from Ambi backend: {:?}" , response. status( ) . as_str( ) )
144+ }
145+ }
146+ Err ( e) => {
147+ match cli. debug {
148+ // Print out the entire reqwest::Error for verbose debugging
149+ true => eprintln ! ( "Response error from Ambi backend: {:?}" , e) ,
150+ // Keep the error reports more succinct
151+ false => {
152+ if e. is_request ( ) {
153+ eprintln ! ( "Response error from Ambi backend: request error" ) ;
154+ } else if e. is_timeout ( ) {
155+ eprintln ! ( "Response error from Ambi backend: request timed out" ) ;
156+ } else {
157+ eprintln ! ( "Response error from Ambi backend: specific error type unknown" ) ;
158+ }
159+ }
160+ }
161+ }
162+ }
163+ }
164+
165+ #[ cfg( test) ]
166+ mod tests {
167+ use super :: * ;
168+ use regex:: Regex ;
169+
170+ #[ test]
171+ fn random_gen_humidity_returns_correctly_formatted_humidity_data ( ) {
172+ let result = random_gen_humidity ( ) ;
173+ let regex = Regex :: new ( r"\d{1,2}.\d{1}" ) . unwrap ( ) ;
174+
175+ assert ! ( regex. is_match( & result) ) ;
176+ }
177+
178+ #[ test]
179+ fn random_gen_temperature_returns_correctly_formatted_humidity_data ( ) {
180+ let result = random_gen_temperature ( ) ;
181+ let regex = Regex :: new ( r"\d{1,2}.\d{1}" ) . unwrap ( ) ;
182+
183+ assert ! ( regex. is_match( & result) ) ;
184+ }
185+
186+ #[ test]
187+ fn random_gen_pressure_returns_correctly_formatted_pressure_data ( ) {
188+ let result = random_gen_pressure ( ) ;
189+ let regex = Regex :: new ( r"\d{3,4}" ) . unwrap ( ) ;
190+ assert ! ( regex. is_match( & result) ) ;
191+ }
192+
193+ #[ test]
194+ fn random_gen_dust_concentration_returns_correctly_formatted_pressure_data ( ) {
195+ let result = random_gen_dust_concentration ( ) ;
196+ let regex = Regex :: new ( r"\d{0,4}" ) . unwrap ( ) ;
197+ assert ! ( regex. is_match( & result) ) ;
198+ }
199+
200+ #[ test]
201+ fn air_purity_from_value_returns_correct_enum ( ) {
202+ let mut rng = thread_rng ( ) ;
203+ let fresh_air = rng. gen_range ( 0 ..=50 ) ;
204+ let low = rng. gen_range ( 51 ..=100 ) ;
205+ let high = rng. gen_range ( 101 ..=150 ) ;
206+ let dangerous = rng. gen_range ( 151 ..u16:: MAX ) ;
207+
208+ assert_eq ! ( AirPurity :: from_value( fresh_air) , AirPurity :: FreshAir ) ;
209+ assert_eq ! ( AirPurity :: from_value( low) , AirPurity :: Low ) ;
210+ assert_eq ! ( AirPurity :: from_value( high) , AirPurity :: High ) ;
211+ assert_eq ! ( AirPurity :: from_value( dangerous) , AirPurity :: Dangerous ) ;
212+ }
213+ }
0 commit comments