Functional Weave
Code in Python

net.ip-classify@1.0.0

impl/rust.rs

2,794 bytes · the Rust implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

use super::funejson::Value;  ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
use super::net_cidr::cidr_contains;  ← from net.cidr ^1.0.0 · built alongside by fune
use super::net_ip_classify_data::{SpecialBlock, SPECIAL_BLOCKS};  ← this capability’s own data, compiled from data/special-purpose.json into the same file by fune build
use super::net_ipv4::is_ipv4;  ← from net.ipv4 ^1.0.0 · built alongside by fune
use super::net_ipv6::{format_ipv6, is_ipv6, parse_ipv6};  ← from net.ipv6 ^1.0.0 · built alongside by fune

/// Which special-purpose block an address is in.
///
/// From the IANA IPv4 and IPv6 Special-Purpose Address Registries (RFC 6890)
/// plus the multicast ranges. The most specific block wins: 192.0.0.9 is Port
/// Control Protocol Anycast (globally reachable), not the /24 of IETF
/// assignments around it (not). An address in no block is "global".
///
/// # Panics
/// Panics when the text is not an IPv4 or IPv6 address.
pub fn classify_ip(address: &str) -> IpClassification {
    let (version, canonical) = if is_ipv4(address) {
        (4, address.to_string())
    } else if is_ipv6(address) {
        (6, format_ipv6(&parse_ipv6(address)))
    } else {
        panic!("\"{}\" is not an IPv4 or IPv6 address", address);
    };
    let mut best: Option<&SpecialBlock> = None;
    let mut best_prefix: i64 = -1;
    for row in SPECIAL_BLOCKS {
        if row.block.contains(':') != (version == 6) {
            continue;
        }
        if !cidr_contains(row.block, &canonical) {
            continue;
        }
        let prefix: i64 = row.block[row.block.find('/').unwrap() + 1..].parse().unwrap();
        if prefix > best_prefix {
            best = Some(row);
            best_prefix = prefix;
        }
    }
    match best {
        None => IpClassification {
            address: canonical,
            version,
            category: "global".to_string(),
            name: "Global unicast".to_string(),
            block: None,
            rfc: None,
            globally_reachable: Some(true),
        },
        Some(row) => IpClassification {
            address: canonical,
            version,
            category: row.category.to_string(),
            name: row.name.to_string(),
            block: Some(row.block.to_string()),
            rfc: Some(row.rfc.to_string()),
            globally_reachable: row.globally_reachable,
        },
    }
}

pub fn ip_classification_to_value(c: &IpClassification) -> Value {
    let opt = |s: &Option<String>| match s {
        Some(s) => Value::str(s),
        None => Value::Null,
    };
    Value::obj(vec![
        ("address", Value::str(&c.address)),
        ("version", Value::Int(c.version)),
        ("category", Value::str(&c.category)),
        ("name", Value::str(&c.name)),
        ("block", opt(&c.block)),
        ("rfc", opt(&c.rfc)),
        ("globallyReachable", match c.globally_reachable {
            Some(b) => Value::Bool(b),
            None => Value::Null,
        }),
    ])
}

pub fn fune_vector(args: &[Value]) -> Value {
    ip_classification_to_value(&classify_ip(args[0].as_str()))
}