Skip to main content

use_port/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4const SERVICE_PORTS: &[(&str, u16)] = &[
5    ("ftp", 21),
6    ("ssh", 22),
7    ("smtp", 25),
8    ("dns", 53),
9    ("http", 80),
10    ("pop3", 110),
11    ("ntp", 123),
12    ("imap", 143),
13    ("ldap", 389),
14    ("https", 443),
15    ("mysql", 3306),
16    ("postgres", 5432),
17    ("redis", 6379),
18];
19
20/// Stores a validated port number.
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub struct Port {
23    /// Port value.
24    pub value: u16,
25}
26
27/// Classifies a port into the standard IANA port ranges.
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum PortRange {
30    /// Ports 0 through 1023.
31    System,
32    /// Ports 1024 through 49151.
33    Registered,
34    /// Ports 49152 through 65535.
35    Dynamic,
36}
37
38/// Returns `true` when the input fits into a valid `u16` port number.
39pub fn is_valid_port(input: u32) -> bool {
40    input <= u16::MAX as u32
41}
42
43/// Parses a port number from text.
44pub fn parse_port(input: &str) -> Option<Port> {
45    let trimmed = input.trim();
46
47    if trimmed.is_empty() {
48        return None;
49    }
50
51    trimmed.parse::<u16>().ok().map(|value| Port { value })
52}
53
54/// Returns the IANA port range for the provided port.
55pub fn port_range(port: u16) -> PortRange {
56    match port {
57        0..=1023 => PortRange::System,
58        1024..=49151 => PortRange::Registered,
59        _ => PortRange::Dynamic,
60    }
61}
62
63/// Returns `true` when the port is in the system range.
64pub fn is_system_port(port: u16) -> bool {
65    matches!(port_range(port), PortRange::System)
66}
67
68/// Returns `true` when the port is in the registered range.
69pub fn is_registered_port(port: u16) -> bool {
70    matches!(port_range(port), PortRange::Registered)
71}
72
73/// Returns `true` when the port is in the dynamic range.
74pub fn is_dynamic_port(port: u16) -> bool {
75    matches!(port_range(port), PortRange::Dynamic)
76}
77
78/// Looks up a common service name for a port.
79pub fn common_port_name(port: u16) -> Option<&'static str> {
80    SERVICE_PORTS
81        .iter()
82        .find(|(_, candidate_port)| *candidate_port == port)
83        .map(|(service, _)| *service)
84}
85
86/// Looks up a default port for a common service name.
87pub fn default_port_for_service(service: &str) -> Option<u16> {
88    let normalized = service.trim().to_ascii_lowercase();
89
90    SERVICE_PORTS
91        .iter()
92        .find(|(candidate_service, _)| *candidate_service == normalized)
93        .map(|(_, port)| *port)
94}