Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add #[serde(skip_serializing_if = "Option::is_none")] to all fields #79

Merged
merged 1 commit into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.10"
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"}}]}"#
);
}
Loading