use crate::version::SemVer;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::str::FromStr;
use thiserror::Error;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum CapabilitySlot {
Deployer,
Secrets,
Telemetry,
Sessions,
State,
Revocation,
}
impl CapabilitySlot {
pub const ALL: &'static [CapabilitySlot] = &[
CapabilitySlot::Deployer,
CapabilitySlot::Secrets,
CapabilitySlot::Telemetry,
CapabilitySlot::Sessions,
CapabilitySlot::State,
CapabilitySlot::Revocation,
];
pub fn as_str(self) -> &'static str {
match self {
CapabilitySlot::Deployer => "deployer",
CapabilitySlot::Secrets => "secrets",
CapabilitySlot::Telemetry => "telemetry",
CapabilitySlot::Sessions => "sessions",
CapabilitySlot::State => "state",
CapabilitySlot::Revocation => "revocation",
}
}
}
impl fmt::Display for CapabilitySlot {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(try_from = "String", into = "String")]
pub struct PackDescriptor {
raw: String,
path: String,
version: SemVer,
}
impl PackDescriptor {
pub fn try_new(raw: impl Into<String>) -> Result<Self, PackDescriptorParseError> {
let raw = raw.into();
Self::parse(&raw).map(|(path, version)| Self { raw, path, version })
}
fn parse(s: &str) -> Result<(String, SemVer), PackDescriptorParseError> {
let mut parts = s.splitn(2, '@');
let path = parts.next().unwrap_or("");
let version = parts
.next()
.ok_or(PackDescriptorParseError::MissingVersion)?;
if parts.next().is_some() {
return Err(PackDescriptorParseError::MultipleAt);
}
if path.is_empty() {
return Err(PackDescriptorParseError::EmptyPath);
}
if !path.contains('.') {
return Err(PackDescriptorParseError::PathMissingDot);
}
for ch in path.chars() {
if !(ch.is_ascii_lowercase() || ch.is_ascii_digit() || ch == '-' || ch == '.') {
return Err(PackDescriptorParseError::InvalidPathChar(ch));
}
}
let version = version
.parse::<SemVer>()
.map_err(|err| PackDescriptorParseError::InvalidSemver(err.to_string()))?;
Ok((path.to_string(), version))
}
pub fn as_str(&self) -> &str {
&self.raw
}
pub fn path(&self) -> &str {
&self.path
}
pub fn version(&self) -> &SemVer {
&self.version
}
}
impl fmt::Display for PackDescriptor {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.raw)
}
}
impl FromStr for PackDescriptor {
type Err = PackDescriptorParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_new(s)
}
}
impl TryFrom<String> for PackDescriptor {
type Error = PackDescriptorParseError;
fn try_from(value: String) -> Result<Self, Self::Error> {
Self::try_new(value)
}
}
impl From<PackDescriptor> for String {
fn from(value: PackDescriptor) -> Self {
value.raw
}
}
#[derive(Debug, Error, PartialEq, Eq)]
pub enum PackDescriptorParseError {
#[error("pack descriptor missing `@<semver>` suffix")]
MissingVersion,
#[error("pack descriptor contains more than one `@` separator")]
MultipleAt,
#[error("pack descriptor path is empty")]
EmptyPath,
#[error("pack descriptor path must contain at least one `.`")]
PathMissingDot,
#[error("pack descriptor path contains invalid character `{0}`")]
InvalidPathChar(char),
#[error("pack descriptor version is not valid SemVer: {0}")]
InvalidSemver(String),
}