@@ -65,8 +65,6 @@ use zbus::MatchRule;
6565use super :: prelude:: * ;
6666use crate :: util:: { country_flag_from_iso_code, new_system_dbus_connection} ;
6767
68- const API_ENDPOINT : & str = "https://ipapi.co/json/" ;
69-
7068#[ derive( Deserialize , Debug , SmartDefault ) ]
7169#[ serde( deny_unknown_fields, default ) ]
7270pub struct Config {
@@ -137,40 +135,75 @@ pub async fn run(config: &Config, api: &CommonApi) -> Result<()> {
137135 } ;
138136
139137 loop {
140- let fetch_info = || IPAddressInfo :: new ( client ) ;
138+ let fetch_info = || api . find_ip_location ( client , Duration :: from_secs ( 0 ) ) ;
141139 let info = fetch_info. retry ( ExponentialBuilder :: default ( ) ) . await ?;
142140
143141 let mut values = map ! {
144142 "ip" => Value :: text( info. ip) ,
145- "version" => Value :: text( info. version) ,
146143 "city" => Value :: text( info. city) ,
147- "region" => Value :: text( info. region) ,
148- "region_code" => Value :: text( info. region_code) ,
149- "country" => Value :: text( info. country) ,
150- "country_name" => Value :: text( info. country_name) ,
151- "country_flag" => Value :: text( country_flag_from_iso_code( & info. country_code) ) ,
152- "country_code" => Value :: text( info. country_code) ,
153- "country_code_iso3" => Value :: text( info. country_code_iso3) ,
154- "country_capital" => Value :: text( info. country_capital) ,
155- "country_tld" => Value :: text( info. country_tld) ,
156- "continent_code" => Value :: text( info. continent_code) ,
157144 "latitude" => Value :: number( info. latitude) ,
158145 "longitude" => Value :: number( info. longitude) ,
159- "timezone" => Value :: text( info. timezone) ,
160- "utc_offset" => Value :: text( info. utc_offset) ,
161- "country_calling_code" => Value :: text( info. country_calling_code) ,
162- "currency" => Value :: text( info. currency) ,
163- "currency_name" => Value :: text( info. currency_name) ,
164- "languages" => Value :: text( info. languages) ,
165- "country_area" => Value :: number( info. country_area) ,
166- "country_population" => Value :: number( info. country_population) ,
167- "asn" => Value :: text( info. asn) ,
168- "org" => Value :: text( info. org) ,
169146 } ;
170- info. postal
171- . map ( |x| values. insert ( "postal" . into ( ) , Value :: text ( x) ) ) ;
172- if info. in_eu {
173- values. insert ( "in_eu" . into ( ) , Value :: flag ( ) ) ;
147+
148+ macro_rules! map_push_if_some { ( $( $key: ident: $type: ident) ,* $( , ) ?) => {
149+ $( {
150+ let key = stringify!( $key) ;
151+ if let Some ( value) = info. $key {
152+ values. insert( key. into( ) , Value :: $type( value) ) ;
153+ } else if format. contains_key( key) {
154+ return Err ( Error :: new( format!(
155+ "The format string contains '{key}', but the {key} field is not provided by {} (an api key may be required)" ,
156+ api. locator_name( )
157+ ) ) ) ;
158+ }
159+ } ) *
160+ } }
161+
162+ map_push_if_some ! (
163+ version: text,
164+ region: text,
165+ region_code: text,
166+ country: text,
167+ country_name: text,
168+ country_code_iso3: text,
169+ country_capital: text,
170+ country_tld: text,
171+ continent_code: text,
172+ postal: text,
173+ timezone: text,
174+ utc_offset: text,
175+ country_calling_code: text,
176+ currency: text,
177+ currency_name: text,
178+ languages: text,
179+ country_area: number,
180+ country_population: number,
181+ asn: text,
182+ org: text,
183+ ) ;
184+
185+ if let Some ( country_code) = info. country_code {
186+ values. insert (
187+ "country_flag" . into ( ) ,
188+ Value :: text ( country_flag_from_iso_code ( & country_code) ) ,
189+ ) ;
190+ values. insert ( "country_code" . into ( ) , Value :: text ( country_code) ) ;
191+ } else if format. contains_key ( "country_code" ) || format. contains_key ( "country_flag" ) {
192+ return Err ( Error :: new ( format ! (
193+ "The format string contains 'country_code' or 'country_flag', but the country_code field is not provided by {}" ,
194+ api. locator_name( )
195+ ) ) ) ;
196+ }
197+
198+ if let Some ( in_eu) = info. in_eu {
199+ if in_eu {
200+ values. insert ( "in_eu" . into ( ) , Value :: flag ( ) ) ;
201+ }
202+ } else if format. contains_key ( "in_eu" ) {
203+ return Err ( Error :: new ( format ! (
204+ "The format string contains 'in_eu', but the in_eu field is not provided by {}" ,
205+ api. locator_name( )
206+ ) ) ) ;
174207 }
175208
176209 let mut widget = Widget :: new ( ) . with_format ( format. clone ( ) ) ;
@@ -184,54 +217,3 @@ pub async fn run(config: &Config, api: &CommonApi) -> Result<()> {
184217 }
185218 }
186219}
187-
188- #[ derive( Deserialize , Default ) ]
189- #[ serde( default ) ]
190- struct IPAddressInfo {
191- error : bool ,
192- reason : String ,
193- ip : String ,
194- version : String ,
195- city : String ,
196- region : String ,
197- region_code : String ,
198- country : String ,
199- country_name : String ,
200- country_code : String ,
201- country_code_iso3 : String ,
202- country_capital : String ,
203- country_tld : String ,
204- continent_code : String ,
205- in_eu : bool ,
206- postal : Option < String > ,
207- latitude : f64 ,
208- longitude : f64 ,
209- timezone : String ,
210- utc_offset : String ,
211- country_calling_code : String ,
212- currency : String ,
213- currency_name : String ,
214- languages : String ,
215- country_area : f64 ,
216- country_population : f64 ,
217- asn : String ,
218- org : String ,
219- }
220-
221- impl IPAddressInfo {
222- async fn new ( client : & reqwest:: Client ) -> Result < Self > {
223- let info: Self = client
224- . get ( API_ENDPOINT )
225- . send ( )
226- . await
227- . error ( "Failed to request current location" ) ?
228- . json :: < Self > ( )
229- . await
230- . error ( "Failed to parse JSON" ) ?;
231- if info. error {
232- Err ( Error :: new ( info. reason ) )
233- } else {
234- Ok ( info)
235- }
236- }
237- }
0 commit comments