use std::path::PathBuf;
use std::str::FromStr;
use url::Url;
use crate::config::{
Binding,
ConfigOrDefinition,
ConfigurationTreeNode,
ResourceDefinition,
TcpPort,
UdpPort,
UrlResource,
Volume,
};
use crate::WickConfiguration;
#[derive(Debug, Clone, serde::Serialize)]
#[non_exhaustive]
pub struct Audit {
pub name: String,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub resources: Vec<AuditedResourceBinding>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub imports: Vec<Audit>,
}
impl Audit {
pub fn new(tree: &ConfigurationTreeNode<WickConfiguration>) -> Self {
Self {
name: tree.name.clone(),
resources: tree
.element
.resources()
.iter()
.map(AuditedResourceBinding::from)
.collect::<Vec<_>>(),
imports: tree.children.iter().map(Self::config_or_def).collect::<Vec<_>>(),
}
}
pub fn new_flattened(elements: &[ConfigOrDefinition<WickConfiguration>]) -> Vec<Audit> {
elements.iter().map(Self::config_or_def).collect::<Vec<_>>()
}
pub(crate) fn config_or_def(el: &ConfigOrDefinition<WickConfiguration>) -> Self {
match el {
crate::config::ConfigOrDefinition::Config(c) => Audit::new(c),
crate::config::ConfigOrDefinition::Definition { id, .. } => Audit {
name: id.clone(),
resources: Vec::new(),
imports: Vec::new(),
},
}
}
}
impl From<&ResourceDefinition> for AuditedResource {
fn from(value: &ResourceDefinition) -> Self {
match value {
ResourceDefinition::TcpPort(v) => Self::TcpPort(AuditedPort {
port: *v.port.value_unchecked(),
address: v.host.value_unchecked().clone(),
}),
ResourceDefinition::UdpPort(v) => Self::UdpPort(AuditedPort {
port: *v.port.value_unchecked(),
address: v.host.value_unchecked().clone(),
}),
ResourceDefinition::Url(v) => Self::Url(AuditedUrl::from(v.url.value_unchecked().clone())),
ResourceDefinition::Volume(v) => Self::Volume(AuditedVolume {
path: v.path().unwrap(),
}),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize)]
pub struct AuditedResourceBinding {
pub(crate) name: String,
pub(crate) resource: AuditedResource,
}
impl From<AuditedResourceBinding> for ResourceDefinition {
fn from(value: AuditedResourceBinding) -> Self {
match value.resource {
AuditedResource::TcpPort(v) => Self::TcpPort(TcpPort::new(v.address, v.port)),
AuditedResource::UdpPort(v) => Self::UdpPort(UdpPort::new(v.address, v.port)),
AuditedResource::Url(v) => Self::Url(UrlResource::new(v.url)),
AuditedResource::Volume(v) => Self::Volume(Volume::new(v.path.to_string_lossy().to_string())),
}
}
}
impl std::fmt::Display for AuditedResourceBinding {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}: {}", self.name, self.resource)
}
}
impl From<&Binding<ResourceDefinition>> for AuditedResourceBinding {
fn from(value: &Binding<ResourceDefinition>) -> Self {
Self {
name: value.id.clone(),
resource: AuditedResource::from(&value.kind),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize)]
#[serde(tag = "kind")]
pub enum AuditedResource {
#[serde(rename = "wick/resource/tcpport@v1")]
TcpPort(AuditedPort),
#[serde(rename = "wick/resource/udpport@v1")]
UdpPort(AuditedPort),
#[serde(rename = "wick/resource/url@v1")]
Url(AuditedUrl),
#[serde(rename = "wick/resource/volume@v1")]
Volume(AuditedVolume),
}
impl std::fmt::Display for AuditedResource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AuditedResource::TcpPort(v) => v.fmt(f),
AuditedResource::UdpPort(v) => v.fmt(f),
AuditedResource::Url(v) => v.fmt(f),
AuditedResource::Volume(v) => v.fmt(f),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize)]
pub struct AuditedPort {
pub(crate) port: u16,
pub(crate) address: String,
}
impl std::fmt::Display for AuditedPort {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:{}", self.address, self.port)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize)]
pub struct AuditedVolume {
pub(crate) path: PathBuf,
}
impl std::fmt::Display for AuditedVolume {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.path.to_string_lossy())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize)]
pub struct AuditedUrl {
pub(crate) url: Url,
}
impl From<Url> for AuditedUrl {
fn from(mut url: Url) -> Self {
let _ = url.set_username("");
let _ = url.set_password(None);
Self { url }
}
}
impl FromStr for AuditedUrl {
type Err = url::ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut url = Url::parse(s)?;
let _ = url.set_username("");
let _ = url.set_password(None);
Ok(Self { url })
}
}
impl std::fmt::Display for AuditedUrl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.url.as_str())
}
}