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