cupnp/
lib.rs

1use std::str::FromStr;
2
3use anyhow::{Result, anyhow};
4pub use easy_upnp::{PortMappingProtocol, UpnpConfig};
5
6#[derive(Debug, Clone, Default)]
7pub enum Protocol {
8	#[default]
9	TCP,
10	UDP,
11}
12
13impl std::fmt::Display for Protocol {
14	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15		f.write_str(match self {
16			Self::UDP => "udp",
17			Self::TCP => "tcp",
18		})
19	}
20}
21
22impl From<Protocol> for PortMappingProtocol {
23	fn from(value: Protocol) -> Self {
24		match value {
25			Protocol::TCP => Self::TCP,
26			Protocol::UDP => Self::UDP,
27		}
28	}
29}
30
31impl FromStr for Protocol {
32	type Err = anyhow::Error;
33
34	fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
35		match s.to_lowercase().as_str() {
36			"tcp" => Ok(Protocol::TCP),
37			"udp" => Ok(Protocol::UDP),
38			x => Err(anyhow!("Invalid Protocol `{}`", x)),
39		}
40	}
41}
42
43fn port_forward(port: u16, protocol: Protocol, duration: Option<u32>) -> UpnpConfig {
44	UpnpConfig {
45		port,
46		protocol: protocol.into(),
47		address: None,
48		duration: duration.unwrap_or(3600),
49		comment: "Forward from cupnp CLI tool".into(),
50	}
51}
52
53fn results(res: Vec<Result<(), easy_upnp::Error>>) -> Result<()> {
54	for result in res {
55		result?
56	}
57
58	Ok(())
59}
60
61pub fn expose_port(port: u16, protocol: Protocol, duration: Option<u32>) -> Result<()> {
62	results(easy_upnp::add_ports([port_forward(port, protocol, duration)]).collect())
63}
64
65pub fn delete_port(port: u16, protocol: Protocol) -> Result<()> {
66	results(easy_upnp::delete_ports([port_forward(port, protocol, None)]).collect())
67}