use std::fmt;
use serde::{Deserialize, Serialize};
use serde::de::{self, Deserializer};
use crate::http::Header;
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct Ident(Option<String>);
macro_rules! ident {
($value:expr) => {
{
#[allow(unknown_lints, eq_op)]
const _: [(); 0 - !{
const ASSERT: bool = $crate::http::Header::is_valid_value($value, false);
ASSERT
} as usize] = [];
$crate::config::Ident::try_new($value).unwrap()
}
}
}
impl Ident {
pub fn try_new<S: Into<String>>(ident: S) -> Result<Ident, String> {
let ident = ident.into();
if !Header::is_valid_value(&ident, false) {
return Err(ident);
}
Ok(Ident(Some(ident)))
}
pub const fn none() -> Ident {
Ident(None)
}
pub fn as_str(&self) -> Option<&str> {
self.0.as_deref()
}
}
impl<'de> Deserialize<'de> for Ident {
fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
type Value = Ident;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a server ident string or `false`")
}
fn visit_bool<E: de::Error>(self, v: bool) -> Result<Self::Value, E> {
if !v {
return Ok(Ident::none());
}
Err(E::invalid_value(de::Unexpected::Bool(v), &self))
}
fn visit_some<D>(self, de: D) -> Result<Self::Value, D::Error>
where D: Deserializer<'de>
{
de.deserialize_string(self)
}
fn visit_none<E: de::Error>(self) -> Result<Self::Value, E> {
Ok(Ident::none())
}
fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
Ok(Ident::none())
}
fn visit_string<E: de::Error>(self, v: String) -> Result<Self::Value, E> {
Ident::try_new(v)
.map_err(|s| E::invalid_value(de::Unexpected::Str(&s), &self))
}
fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
self.visit_string(v.into())
}
}
de.deserialize_string(Visitor)
}
}
impl fmt::Display for Ident {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.as_str() {
Some(name) => name.fmt(f),
None => "disabled".fmt(f),
}
}
}
impl Default for Ident {
fn default() -> Self {
ident!("Rocket")
}
}