use crate::http::{HeaderName, HeaderValue, Response};
use crate::layer::MapOutputLayer;
use crate::net::address::ip::geo::GeoLocation;
const GEO_ATTRIBUTION_HEADER: &str = "x-geo-attribution";
pub fn geo_attribution_layer(
notices: Vec<&'static str>,
) -> MapOutputLayer<impl Fn(Response) -> Response + Clone + Send + Sync + 'static> {
let name = HeaderName::from_static(GEO_ATTRIBUTION_HEADER);
MapOutputLayer::new(move |mut resp: Response| {
for notice in ¬ices {
resp.headers_mut()
.append(name.clone(), HeaderValue::from_static(notice));
}
resp
})
}
#[must_use]
pub fn geo_attribution_html_comment(notices: &[&str]) -> Option<String> {
(!notices.is_empty()).then(|| {
let body = notices.join(" | ").replace("--", "- -");
format!("<!-- {body} -->")
})
}
#[must_use]
pub fn geo_location_rows(loc: &GeoLocation) -> Vec<(&'static str, String)> {
let mut rows = Vec::new();
if let Some(c) = &loc.country {
let name = c
.name()
.map(str::to_owned)
.unwrap_or_else(|| c.code().to_owned());
rows.push(("Country", format!("{name} ({})", c.code())));
}
if let Some(c) = &loc.continent {
rows.push((
"Continent",
c.name()
.map(str::to_owned)
.unwrap_or_else(|| c.code().to_owned()),
));
}
if let Some(region) = loc.subdivisions.first().and_then(|s| s.name.as_deref()) {
rows.push(("Region", region.to_owned()));
}
if let Some(city) = &loc.city {
rows.push(("City", city.to_string()));
}
if let Some(postal) = &loc.postal_code {
rows.push(("Postal", postal.to_string()));
}
if let Some(l) = &loc.location {
rows.push((
"Coordinates",
format!("{:.4}, {:.4}", l.latitude, l.longitude),
));
if let Some(tz) = &l.time_zone {
rows.push(("Time Zone", tz.to_string()));
}
}
if let Some(asys) = &loc.autonomous_system {
if let Some(asn) = asys.asn {
rows.push(("ASN", format!("AS{}", asn.as_u32())));
}
if let Some(org) = &asys.organization {
rows.push(("Network", org.to_string()));
}
}
rows
}