use crate::error::SamlError;
pub const MAX_RELAY_STATE_BYTES: usize = 80;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct RelayState(String);
impl RelayState {
pub fn try_new(value: impl Into<String>) -> Result<Self, SamlError> {
let value = value.into();
validate_relay_state_bytes(&value)?;
Ok(Self(value))
}
pub fn as_str(&self) -> &str {
&self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum RelayStateParam {
Absent,
PresentEmpty,
PresentValue(RelayState),
}
impl RelayStateParam {
pub fn absent() -> Self {
Self::Absent
}
pub fn present_empty() -> Self {
Self::PresentEmpty
}
pub fn try_from_option(value: Option<impl Into<String>>) -> Result<Self, SamlError> {
match value {
None => Ok(Self::Absent),
Some(value) => {
let value = value.into();
if value.is_empty() {
Ok(Self::PresentEmpty)
} else {
Ok(Self::PresentValue(RelayState::try_new(value)?))
}
}
}
}
pub fn as_deref(&self) -> Option<&str> {
match self {
Self::Absent => None,
Self::PresentEmpty => Some(""),
Self::PresentValue(value) => Some(value.as_str()),
}
}
pub(crate) fn validate(&self) -> Result<(), SamlError> {
if matches!(self, Self::PresentValue(value) if value.as_str().is_empty()) {
return Err(SamlError::Invalid(
"RelayState PresentValue must not be empty".into(),
));
}
if let Some(value) = self.as_deref() {
validate_relay_state_bytes(value)?;
}
Ok(())
}
}
impl TryFrom<Option<String>> for RelayStateParam {
type Error = SamlError;
fn try_from(value: Option<String>) -> Result<Self, Self::Error> {
Self::try_from_option(value)
}
}
fn validate_relay_state_bytes(value: &str) -> Result<(), SamlError> {
if value.len() > MAX_RELAY_STATE_BYTES {
return Err(SamlError::Invalid(
"RelayState exceeds SAML Bindings 80-byte limit".into(),
));
}
Ok(())
}