wick_trigger/
resources.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
use std::net::{IpAddr, SocketAddr};
use std::path::PathBuf;
use std::str::FromStr;

use url::Url;
use wick_config::config::{ResourceDefinition, TcpPort, UdpPort, UrlResource, Volume};

#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum ResourceError {
  #[error("Invalid IP address '{0}': {1}")]
  InvalidIpAddress(String, String),
  #[error("Invalid path: {0}")]
  InvalidPath(String),
}

#[derive(Debug, Clone, PartialEq)]
#[allow(missing_copy_implementations)]
#[allow(clippy::exhaustive_enums)]
pub enum Resource {
  TcpPort(SocketAddr),
  UdpPort(SocketAddr),
  Url(Url),
  Volume(PathBuf),
}

#[derive(Debug, Clone, Copy, PartialEq)]
#[must_use]
#[allow(clippy::exhaustive_enums)]
pub enum ResourceKind {
  TcpPort,
  UdpPort,
  Url,
  Volume,
}

impl std::fmt::Display for ResourceKind {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    match self {
      Self::TcpPort => write!(f, "TcpPort"),
      Self::UdpPort => write!(f, "UdpPort"),
      Self::Url => write!(f, "Url"),
      Self::Volume => write!(f, "Volume"),
    }
  }
}

impl Resource {
  pub fn new(config: ResourceDefinition) -> Result<Self, ResourceError> {
    match config {
      ResourceDefinition::TcpPort(config) => Self::new_tcp_port(&config),
      ResourceDefinition::UdpPort(config) => Self::new_udp_port(&config),
      ResourceDefinition::Url(config) => Self::new_url(&config),
      ResourceDefinition::Volume(config) => Self::new_volume(&config),
    }
  }

  pub fn new_tcp_port(config: &TcpPort) -> Result<Self, ResourceError> {
    let host = config.host().value_unchecked();
    let port = config.port().value_unchecked();
    Ok(Self::TcpPort(SocketAddr::new(
      IpAddr::from_str(host).map_err(|e| ResourceError::InvalidIpAddress(host.clone(), e.to_string()))?,
      *port,
    )))
  }

  pub fn new_udp_port(config: &UdpPort) -> Result<Self, ResourceError> {
    let host = config.host().value_unchecked();
    let port = config.port().value_unchecked();
    Ok(Self::UdpPort(SocketAddr::new(
      IpAddr::from_str(host).map_err(|e| ResourceError::InvalidIpAddress(host.clone(), e.to_string()))?,
      *port,
    )))
  }

  pub fn new_url(config: &UrlResource) -> Result<Self, ResourceError> {
    Ok(Self::Url(config.url().value_unchecked().clone()))
  }

  pub fn new_volume(config: &Volume) -> Result<Self, ResourceError> {
    Ok(Self::Volume(
      config.path().map_err(|e| ResourceError::InvalidPath(e.to_string()))?,
    ))
  }

  pub const fn kind(&self) -> ResourceKind {
    match self {
      Self::TcpPort(_) => ResourceKind::TcpPort,
      Self::UdpPort(_) => ResourceKind::UdpPort,
      Self::Url(_) => ResourceKind::Url,
      Self::Volume(_) => ResourceKind::Volume,
    }
  }
}

#[cfg(test)]
mod test {
  use anyhow::Result;

  use super::*;

  #[test]
  fn test_basic() -> Result<()> {
    let resource = Resource::new_tcp_port(&TcpPort::new("0.0.0.0", 8888))?;
    assert_eq!(
      resource,
      Resource::TcpPort(SocketAddr::new(IpAddr::from_str("0.0.0.0")?, 8888))
    );

    Ok(())
  }
}