use crate::{ids::CanisterRole, shared_support::is_ascii_snake_case};
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CanisterRoleNameIssue {
Empty,
InvalidSnakeCase,
TooLong { max_bytes: usize },
}
impl fmt::Display for CanisterRoleNameIssue {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Empty => formatter.write_str("must not be empty"),
Self::InvalidSnakeCase => formatter.write_str(
"must use lowercase snake_case beginning with an ASCII letter, with nonempty lowercase alphanumeric words separated by single '_' characters",
),
Self::TooLong { max_bytes } => {
write!(formatter, "must not exceed {max_bytes} bytes")
}
}
}
}
pub const fn validate_canister_role_name(role: &str) -> Result<(), CanisterRoleNameIssue> {
let bytes = role.as_bytes();
if bytes.is_empty() {
return Err(CanisterRoleNameIssue::Empty);
}
if bytes.len() > super::NAME_MAX_BYTES {
return Err(CanisterRoleNameIssue::TooLong {
max_bytes: super::NAME_MAX_BYTES,
});
}
if !is_ascii_snake_case(role) {
return Err(CanisterRoleNameIssue::InvalidSnakeCase);
}
Ok(())
}
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct FleetRoleRefV1 {
pub fleet: String,
pub role: CanisterRole,
}
impl FleetRoleRefV1 {
#[must_use]
pub fn new(fleet: impl Into<String>, role: CanisterRole) -> Self {
Self {
fleet: fleet.into(),
role,
}
}
}
impl fmt::Display for FleetRoleRefV1 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}.{}", self.fleet, self.role)
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct RoleDeclaration {
pub kind: RoleDeclarationKind,
pub package: String,
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum RoleDeclarationKind {
Root,
Canister,
}