1use std::path::PathBuf;
2use std::str::FromStr;
3
4use url::Url;
5
6use crate::config::{
7 Binding,
8 ConfigOrDefinition,
9 ConfigurationTreeNode,
10 ResourceDefinition,
11 TcpPort,
12 UdpPort,
13 UrlResource,
14 Volume,
15};
16use crate::WickConfiguration;
17
18#[derive(Debug, Clone, serde::Serialize)]
20#[non_exhaustive]
21pub struct Audit {
22 pub name: String,
24 #[serde(skip_serializing_if = "Vec::is_empty")]
26 pub resources: Vec<AuditedResourceBinding>,
27 #[serde(skip_serializing_if = "Vec::is_empty")]
29 pub imports: Vec<Audit>,
30}
31
32impl Audit {
33 pub fn new(tree: &ConfigurationTreeNode<WickConfiguration>) -> Self {
35 Self {
36 name: tree.name.clone(),
37 resources: tree
38 .element
39 .resources()
40 .iter()
41 .map(AuditedResourceBinding::from)
42 .collect::<Vec<_>>(),
43 imports: tree.children.iter().map(Self::config_or_def).collect::<Vec<_>>(),
44 }
45 }
46
47 pub fn new_flattened(elements: &[ConfigOrDefinition<WickConfiguration>]) -> Vec<Audit> {
49 elements.iter().map(Self::config_or_def).collect::<Vec<_>>()
50 }
51
52 pub(crate) fn config_or_def(el: &ConfigOrDefinition<WickConfiguration>) -> Self {
53 match el {
54 crate::config::ConfigOrDefinition::Config(c) => Audit::new(c),
55 crate::config::ConfigOrDefinition::Definition { id, .. } => Audit {
56 name: id.clone(),
57 resources: Vec::new(),
58 imports: Vec::new(),
59 },
60 }
61 }
62}
63
64impl From<&ResourceDefinition> for AuditedResource {
65 fn from(value: &ResourceDefinition) -> Self {
66 match value {
67 ResourceDefinition::TcpPort(v) => Self::TcpPort(AuditedPort {
68 port: *v.port.value_unchecked(),
69 address: v.host.value_unchecked().clone(),
70 }),
71 ResourceDefinition::UdpPort(v) => Self::UdpPort(AuditedPort {
72 port: *v.port.value_unchecked(),
73 address: v.host.value_unchecked().clone(),
74 }),
75 ResourceDefinition::Url(v) => Self::Url(AuditedUrl::from(v.url.value_unchecked().clone())),
76 ResourceDefinition::Volume(v) => Self::Volume(AuditedVolume {
77 path: v.path().unwrap(),
78 }),
79 }
80 }
81}
82
83#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize)]
85pub struct AuditedResourceBinding {
86 pub(crate) name: String,
87 pub(crate) resource: AuditedResource,
88}
89
90impl From<AuditedResourceBinding> for ResourceDefinition {
91 fn from(value: AuditedResourceBinding) -> Self {
92 match value.resource {
93 AuditedResource::TcpPort(v) => Self::TcpPort(TcpPort::new(v.address, v.port)),
94 AuditedResource::UdpPort(v) => Self::UdpPort(UdpPort::new(v.address, v.port)),
95 AuditedResource::Url(v) => Self::Url(UrlResource::new(v.url)),
96 AuditedResource::Volume(v) => Self::Volume(Volume::new(v.path.to_string_lossy().to_string())),
97 }
98 }
99}
100
101impl std::fmt::Display for AuditedResourceBinding {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 write!(f, "{}: {}", self.name, self.resource)
104 }
105}
106
107impl From<&Binding<ResourceDefinition>> for AuditedResourceBinding {
108 fn from(value: &Binding<ResourceDefinition>) -> Self {
109 Self {
110 name: value.id().to_owned(),
111 resource: AuditedResource::from(&value.kind),
112 }
113 }
114}
115
116#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize)]
117#[serde(tag = "kind")]
118
119pub enum AuditedResource {
121 #[serde(rename = "wick/resource/tcpport@v1")]
123 TcpPort(AuditedPort),
124 #[serde(rename = "wick/resource/udpport@v1")]
126 UdpPort(AuditedPort),
127 #[serde(rename = "wick/resource/url@v1")]
129 Url(AuditedUrl),
130 #[serde(rename = "wick/resource/volume@v1")]
132 Volume(AuditedVolume),
133}
134
135impl std::fmt::Display for AuditedResource {
136 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137 match self {
138 AuditedResource::TcpPort(v) => v.fmt(f),
139 AuditedResource::UdpPort(v) => v.fmt(f),
140 AuditedResource::Url(v) => v.fmt(f),
141 AuditedResource::Volume(v) => v.fmt(f),
142 }
143 }
144}
145
146#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize)]
148pub struct AuditedPort {
149 pub(crate) port: u16,
150 pub(crate) address: String,
151}
152
153impl std::fmt::Display for AuditedPort {
154 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155 write!(f, "{}:{}", self.address, self.port)
156 }
157}
158
159#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize)]
161pub struct AuditedVolume {
162 pub(crate) path: PathBuf,
163}
164
165impl std::fmt::Display for AuditedVolume {
166 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
167 f.write_str(&self.path.to_string_lossy())
168 }
169}
170
171#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize)]
173pub struct AuditedUrl {
174 pub(crate) url: Url,
175}
176
177impl From<Url> for AuditedUrl {
178 fn from(mut url: Url) -> Self {
179 let _ = url.set_username("");
180 let _ = url.set_password(None);
181 Self { url }
182 }
183}
184
185impl FromStr for AuditedUrl {
186 type Err = url::ParseError;
187
188 fn from_str(s: &str) -> Result<Self, Self::Err> {
189 let mut url = Url::parse(s)?;
190 let _ = url.set_username("");
191 let _ = url.set_password(None);
192
193 Ok(Self { url })
194 }
195}
196
197impl std::fmt::Display for AuditedUrl {
198 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199 f.write_str(self.url.as_str())
200 }
201}