opencellid 0.1.0

Rust client library for the OpenCellID API — sync and async clients with tracing, structured errors, and bounded I/O.
Documentation
//! Helpers for safe tracing of requests (API key redaction).

use url::Url;

/// Returns a copy of `url` with the `key` query parameter replaced by `***`.
///
/// The original URL is left untouched. Used in `debug!` logs so that the API
/// key never appears in captured traces.
pub(crate) fn redact_api_key(url: &Url) -> String {
    let mut redacted = url.clone();
    let pairs: Vec<(String, String)> = redacted
        .query_pairs()
        .map(|(k, v)| {
            if k == "key" {
                (k.into_owned(), "***".to_string())
            } else {
                (k.into_owned(), v.into_owned())
            }
        })
        .collect();
    redacted.query_pairs_mut().clear().extend_pairs(pairs.iter().map(|(k, v)| (k.as_str(), v.as_str())));
    redacted.to_string()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn redacts_key_query_param() {
        let u = Url::parse("https://opencellid.org/cell/get?key=SECRET&mcc=250&mnc=1").unwrap();
        let s = redact_api_key(&u);
        assert!(!s.contains("SECRET"));
        assert!(s.contains("key=***"));
        assert!(s.contains("mcc=250"));
    }

    #[test]
    fn leaves_url_without_key_alone() {
        let u = Url::parse("https://opencellid.org/cell/get?mcc=250").unwrap();
        let s = redact_api_key(&u);
        assert!(s.contains("mcc=250"));
    }
}