ipflag 0.1.0

Human-friendly IP -> country flag display core (resolver-pluggable, no data bundled).
Documentation
use crate::CountryCode;
use std::net::IpAddr;

/// Resolver interface for mapping `IpAddr` → `CountryCode`.
///
/// IP Flag does not include geolocation data. To get a real country result, your
/// application (or an extension crate) must implement this trait.
///
/// The resolver is only called for public IPs (`IpScope::Public`). Private and
/// special addresses never call this function.
///
/// # Return value
/// - `Ok(Some(code))`: country found, displayed as `🇰🇷 KR`
/// - `Ok(None)`: unknown country, displayed as `🌐 UNKNOWN`
/// - `Err(e)`: resolver failure (database not found, corrupted data, etc.)
///
/// # Example (demo only)
///
/// ```rust
/// use ipflag::{CountryCode, IpResolver, tag_ip};
/// use std::net::IpAddr;
///
/// struct AlwaysCN;
/// impl IpResolver for AlwaysCN {
///     type Error = core::convert::Infallible;
///     fn resolve(&self, _ip: IpAddr) -> Result<Option<CountryCode>, Self::Error> {
///         Ok(CountryCode::new("CN"))
///     }
/// }
///
/// let tag = tag_ip(&AlwaysCN, "8.8.8.8").unwrap();
/// assert_eq!(tag.to_string(), "🇨🇳 CN");
/// ```
///
/// For real-world usage, back this resolver with a real data source (GeoIP DB).
pub trait IpResolver {
    /// Resolver-specific error type.
    type Error;

    /// Resolve a public IP into a country code.
    fn resolve(&self, ip: IpAddr) -> Result<Option<CountryCode>, Self::Error>;
}

/// A built-in resolver that never resolves any public IP.
///
/// This is provided so IP Flag remains useful out-of-the-box:
/// - private IPs → 🏠 PRIVATE
/// - special IPs → ⚙️ SPECIAL
/// - public IPs → 🌐 UNKNOWN
///
/// Useful for UI scaffolding, tests, or when you only want scope classification.
pub struct NoopResolver;

impl IpResolver for NoopResolver {
    type Error = core::convert::Infallible;

    fn resolve(&self, _ip: IpAddr) -> Result<Option<CountryCode>, Self::Error> {
        Ok(None)
    }
}