Skip to content

Commit

Permalink
Merge pull request #79 from stefansundin/skip_serializing_if
Browse files Browse the repository at this point in the history
Add `#[serde(skip_serializing_if = "Option::is_none")]` to all fields
  • Loading branch information
oschwald authored Apr 9, 2024
2 parents 39cce16 + 65def4b commit b5a6ccc
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ env_logger = "0.11"
criterion = "0.5"
fake = "2.4"
rayon = "1.5"
serde_json = "1.0"

[[bench]]
name = "lookup"
Expand Down
104 changes: 104 additions & 0 deletions src/maxminddb/geoip2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,88 +4,129 @@ use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct Country<'a> {
#[serde(borrow)]
#[serde(skip_serializing_if = "Option::is_none")]
pub continent: Option<country::Continent<'a>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub country: Option<country::Country<'a>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub registered_country: Option<country::Country<'a>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub represented_country: Option<country::RepresentedCountry<'a>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub traits: Option<country::Traits>,
}

/// GeoIP2 City record
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct City<'a> {
#[serde(borrow)]
#[serde(skip_serializing_if = "Option::is_none")]
pub city: Option<city::City<'a>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub continent: Option<city::Continent<'a>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub country: Option<city::Country<'a>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub location: Option<city::Location<'a>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub postal: Option<city::Postal<'a>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub registered_country: Option<city::Country<'a>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub represented_country: Option<city::RepresentedCountry<'a>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub subdivisions: Option<Vec<city::Subdivision<'a>>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub traits: Option<city::Traits>,
}

/// GeoIP2 Enterprise record
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct Enterprise<'a> {
#[serde(borrow)]
#[serde(skip_serializing_if = "Option::is_none")]
pub city: Option<enterprise::City<'a>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub continent: Option<enterprise::Continent<'a>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub country: Option<enterprise::Country<'a>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub location: Option<enterprise::Location<'a>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub postal: Option<enterprise::Postal<'a>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub registered_country: Option<enterprise::Country<'a>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub represented_country: Option<enterprise::RepresentedCountry<'a>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub subdivisions: Option<Vec<enterprise::Subdivision<'a>>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub traits: Option<enterprise::Traits<'a>>,
}

/// GeoIP2 ISP record
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct Isp<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
pub autonomous_system_number: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub autonomous_system_organization: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub isp: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mobile_country_code: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mobile_network_code: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub organization: Option<&'a str>,
}

/// GeoIP2 Connection-Type record
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct ConnectionType<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
pub connection_type: Option<&'a str>,
}

/// GeoIP2 Anonymous Ip record
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct AnonymousIp {
#[serde(skip_serializing_if = "Option::is_none")]
pub is_anonymous: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_anonymous_vpn: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_hosting_provider: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_public_proxy: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_residential_proxy: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_tor_exit_node: Option<bool>,
}

/// GeoIP2 DensityIncome record
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct DensityIncome {
#[serde(skip_serializing_if = "Option::is_none")]
pub average_income: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub population_density: Option<u32>,
}

/// GeoIP2 Domain record
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct Domain<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
pub domain: Option<&'a str>,
}

/// GeoIP2 Asn record
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct Asn<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
pub autonomous_system_number: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub autonomous_system_organization: Option<&'a str>,
}

Expand All @@ -96,33 +137,48 @@ pub mod country {

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct Continent<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
pub code: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub geoname_id: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub names: Option<BTreeMap<&'a str, &'a str>>,
}

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct Country<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
pub geoname_id: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_in_european_union: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub iso_code: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub names: Option<BTreeMap<&'a str, &'a str>>,
}

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct RepresentedCountry<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
pub geoname_id: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_in_european_union: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub iso_code: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub names: Option<BTreeMap<&'a str, &'a str>>,
#[serde(rename = "type")]
#[serde(skip_serializing_if = "Option::is_none")]
pub representation_type: Option<&'a str>,
}

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct Traits {
#[serde(skip_serializing_if = "Option::is_none")]
pub is_anonymous_proxy: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_anycast: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_satellite_provider: Option<bool>,
}
}
Expand All @@ -136,29 +192,40 @@ pub mod city {

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct City<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
pub geoname_id: Option<u32>,
#[serde(borrow)]
#[serde(skip_serializing_if = "Option::is_none")]
pub names: Option<BTreeMap<&'a str, &'a str>>,
}

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct Location<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
pub accuracy_radius: Option<u16>,
#[serde(skip_serializing_if = "Option::is_none")]
pub latitude: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub longitude: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metro_code: Option<u16>,
#[serde(skip_serializing_if = "Option::is_none")]
pub time_zone: Option<&'a str>,
}

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct Postal<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
pub code: Option<&'a str>,
}

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct Subdivision<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
pub geoname_id: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub iso_code: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub names: Option<BTreeMap<&'a str, &'a str>>,
}
}
Expand All @@ -172,63 +239,100 @@ pub mod enterprise {

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct City<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
pub confidence: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub geoname_id: Option<u32>,
#[serde(borrow)]
#[serde(skip_serializing_if = "Option::is_none")]
pub names: Option<BTreeMap<&'a str, &'a str>>,
}

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct Country<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
pub confidence: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub geoname_id: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_in_european_union: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub iso_code: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub names: Option<BTreeMap<&'a str, &'a str>>,
}

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct Location<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
pub accuracy_radius: Option<u16>,
#[serde(skip_serializing_if = "Option::is_none")]
pub latitude: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub longitude: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metro_code: Option<u16>,
#[serde(skip_serializing_if = "Option::is_none")]
pub time_zone: Option<&'a str>,
}

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct Postal<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
pub code: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub confidence: Option<u8>,
}

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct Subdivision<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
pub confidence: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub geoname_id: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub iso_code: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub names: Option<BTreeMap<&'a str, &'a str>>,
}

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct Traits<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
pub autonomous_system_number: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub autonomous_system_organization: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub connection_type: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub domain: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_anonymous: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_anonymous_proxy: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_anonymous_vpn: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_anycast: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_hosting_provider: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub isp: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_public_proxy: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_residential_proxy: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_satellite_provider: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_tor_exit_node: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mobile_country_code: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mobile_network_code: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub organization: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub user_type: Option<&'a str>,
}
}
21 changes: 21 additions & 0 deletions src/maxminddb/reader_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::net::IpAddr;
use std::str::FromStr;

use serde::Deserialize;
use serde_json::json;

use super::{MaxMindDBError, Reader};

Expand Down Expand Up @@ -483,3 +484,23 @@ fn check_ip<T: AsRef<[u8]>>(reader: &Reader<T>, ip_version: usize) {
}
}
}

#[test]
fn test_json_serialize() {
use super::geoip2::City;
let _ = env_logger::try_init();

let filename = "test-data/test-data/GeoIP2-City-Test.mmdb";

let reader = Reader::open_readfile(filename).unwrap();

let ip: IpAddr = FromStr::from_str("89.160.20.112").unwrap();
let city: City = reader.lookup(ip).unwrap();

let json_string = json!(city).to_string();

assert_eq!(
json_string,
r#"{"city":{"geoname_id":2694762,"names":{"de":"Linköping","en":"Linköping","fr":"Linköping","ja":"リンシェーピング","zh-CN":"林雪平"}},"continent":{"code":"EU","geoname_id":6255148,"names":{"de":"Europa","en":"Europe","es":"Europa","fr":"Europe","ja":"ヨーロッパ","pt-BR":"Europa","ru":"Европа","zh-CN":"欧洲"}},"country":{"geoname_id":2661886,"is_in_european_union":true,"iso_code":"SE","names":{"de":"Schweden","en":"Sweden","es":"Suecia","fr":"Suède","ja":"スウェーデン王国","pt-BR":"Suécia","ru":"Швеция","zh-CN":"瑞典"}},"location":{"accuracy_radius":76,"latitude":58.4167,"longitude":15.6167,"time_zone":"Europe/Stockholm"},"registered_country":{"geoname_id":2921044,"is_in_european_union":true,"iso_code":"DE","names":{"de":"Deutschland","en":"Germany","es":"Alemania","fr":"Allemagne","ja":"ドイツ連邦共和国","pt-BR":"Alemanha","ru":"Германия","zh-CN":"德国"}},"subdivisions":[{"geoname_id":2685867,"iso_code":"E","names":{"en":"Östergötland County","fr":"Comté d'Östergötland"}}]}"#
);
}

0 comments on commit b5a6ccc

Please sign in to comment.