use serde::{Deserialize, Serialize};
use super::error::{ApplicationError, ApplicationResult};
use super::identity::LogicalId;
use super::manifest::ApplicationManifest;
#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
pub enum MountSelector {
Command { id: String },
Projector { id: String },
Surface { id: String },
Extension { id: String },
}
impl MountSelector {
pub fn command(id: impl Into<String>) -> ApplicationResult<Self> {
Ok(Self::Command {
id: LogicalId::try_new("command mount", id)?.into_string(),
})
}
pub fn projector(id: impl Into<String>) -> ApplicationResult<Self> {
Ok(Self::Projector {
id: LogicalId::try_new("projector mount", id)?.into_string(),
})
}
pub fn surface(id: impl Into<String>) -> ApplicationResult<Self> {
Ok(Self::Surface {
id: LogicalId::try_new("surface mount", id)?.into_string(),
})
}
pub fn extension(id: impl Into<String>) -> ApplicationResult<Self> {
Ok(Self::Extension {
id: LogicalId::try_new("extension mount", id)?.into_string(),
})
}
pub fn kind_label(&self) -> &'static str {
match self {
Self::Command { .. } => "command",
Self::Projector { .. } => "projector",
Self::Surface { .. } => "surface",
Self::Extension { .. } => "extension",
}
}
pub fn id(&self) -> &str {
match self {
Self::Command { id }
| Self::Projector { id }
| Self::Surface { id }
| Self::Extension { id } => id.as_str(),
}
}
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ProcessPreset {
Full,
Writer,
Projector,
QueryApi,
}
impl ProcessPreset {
pub fn expand(self, manifest: &ApplicationManifest) -> ApplicationResult<Vec<MountSelector>> {
let mut mounts = match self {
Self::Full => {
let mut mounts = Vec::new();
for command in &manifest.commands {
mounts.push(MountSelector::command(command.id.clone())?);
}
for projection in &manifest.projections {
mounts.push(MountSelector::projector(projection.id.clone())?);
}
for surface in &manifest.surfaces {
mounts.push(MountSelector::surface(surface.id.clone())?);
}
for extension in &manifest.extensions {
mounts.push(MountSelector::extension(extension.id.clone())?);
}
mounts
}
Self::Writer => manifest
.commands
.iter()
.map(|command| MountSelector::command(command.id.clone()))
.collect::<ApplicationResult<Vec<_>>>()?,
Self::Projector => manifest
.projections
.iter()
.map(|projection| MountSelector::projector(projection.id.clone()))
.collect::<ApplicationResult<Vec<_>>>()?,
Self::QueryApi => manifest
.surfaces
.iter()
.map(|surface| MountSelector::surface(surface.id.clone()))
.collect::<ApplicationResult<Vec<_>>>()?,
};
mounts.sort();
mounts.dedup();
Ok(mounts)
}
}
pub fn validate_mounts_against_manifest(
manifest: &ApplicationManifest,
mounts: &[MountSelector],
) -> ApplicationResult<()> {
let mut seen = std::collections::BTreeSet::new();
for mount in mounts {
if !seen.insert(mount.clone()) {
return Err(ApplicationError::Duplicate {
kind: "mount selector",
identity: format!("{}:{}", mount.kind_label(), mount.id()),
});
}
match mount {
MountSelector::Command { id } => {
if !manifest.commands.iter().any(|command| command.id == *id) {
return Err(ApplicationError::Missing {
kind: "command mount",
identity: id.clone(),
});
}
}
MountSelector::Projector { id } => {
if !manifest
.projections
.iter()
.any(|projection| projection.id == *id)
{
return Err(ApplicationError::Missing {
kind: "projector mount",
identity: id.clone(),
});
}
}
MountSelector::Surface { id } => {
if !manifest.surfaces.iter().any(|surface| surface.id == *id) {
return Err(ApplicationError::Missing {
kind: "surface mount",
identity: id.clone(),
});
}
}
MountSelector::Extension { id } => {
if !manifest
.extensions
.iter()
.any(|extension| extension.id == *id)
{
return Err(ApplicationError::Missing {
kind: "extension mount",
identity: id.clone(),
});
}
}
}
}
Ok(())
}