use std::fmt;
pub(super) trait Attribute {
const NAME: &'static str;
const VALID_ON: &'static str;
const DUPLICATE: DuplicateAttribute = DuplicateAttribute {
attribute: Self::NAME,
};
const INVALID: OnlyValidOn = OnlyValidOn {
attribute: Self::NAME,
valid_on: Self::VALID_ON,
};
}
pub(super) trait FlagAttribute: Attribute {
const BASE_NAME: &'static str;
const BASE_INVALID: OnlyValidOn = OnlyValidOn {
attribute: Self::BASE_NAME,
valid_on: Self::VALID_ON,
};
}
const VALID_A: &str = "an enum or a struct";
const VALID_B: &str = "an enum or structs with named fields";
const VALID_C: &str = "an enum, enum variants, or a struct with named fields";
const VALID_D: &str = "enum variant or struct fields with a name";
const VALID_E: &str = "enum variants or structs with named fields";
const VALID_F: &str = "enum variants, structs with named fields, or tuple structs";
macro_rules! def_attributes {
($(($name:ident, $attr:expr, $valid:expr)),*$(,)?) => {
$(
pub(super) struct $name;
impl Attribute for $name {
const NAME: &'static str = $attr;
const VALID_ON: &'static str = $valid;
}
)*
};
}
def_attributes![
(Backtrace, "backtrace", VALID_D),
(ContextFlag, "context(bool)", VALID_E),
(ContextName, "context(name)", VALID_E),
(ContextSuffix, "context(suffix)", VALID_E),
(CrateRoot, "crate_root", VALID_A),
(Display, "display", VALID_E),
(Implicit, "implicit", VALID_D),
(Module, "module", VALID_B),
(ProvideExpression, "provide(type => expression)", VALID_F),
(ProvideFlag, "provide(bool)", VALID_D),
(SourceFlag, "source(bool)", VALID_D),
(SourceFrom, "source(from)", VALID_D),
(Transparent, "transparent", VALID_E),
(Visibility, "visibility", VALID_C),
(Whatever, "whatever", VALID_E),
];
macro_rules! def_flag_attributes {
($(($name:ident, $attr:expr)),*$(,)?) => {
$(
impl FlagAttribute for $name {
const BASE_NAME: &'static str = $attr;
}
)*
};
}
def_flag_attributes![
(Backtrace, "backtrace"),
(ContextFlag, "context"),
(Implicit, "implicit"),
(ProvideFlag, "provide"),
(SourceFlag, "source"),
(Transparent, "transparent"),
];
impl Backtrace {
pub(super) const FALSE_ON_WRONG_FIELD: WrongField = WrongField {
attribute: "backtrace(false)",
valid_field: "backtrace",
};
pub(super) const DUPLICATE_FIELD: DuplicateField = DuplicateField { field: Self::NAME };
}
impl Implicit {
pub(super) const FALSE_DOES_NOTHING: DoesNothing = DoesNothing {
attribute: "implicit(false)",
};
}
pub(super) struct Provide;
impl Provide {
pub(super) const FALSE_ON_WRONG_FIELD: WrongField = WrongField {
attribute: "provide(false)",
valid_field: "backtrace",
};
}
pub(super) struct Source;
impl Source {
const NAME: &'static str = "source";
const FALSE: &'static str = "source(false)";
pub(super) const FALSE_ON_WRONG_FIELD: WrongField = WrongField {
attribute: Self::FALSE,
valid_field: Self::NAME,
};
pub(super) const FALSE_AND_FROM_INCOMPATIBLE: IncompatibleAttributes<'static> =
IncompatibleAttributes::new(&[Self::FALSE, SourceFrom::NAME]);
pub(super) const DUPLICATE_FIELD: DuplicateField = DuplicateField { field: Self::NAME };
}
impl Transparent {
pub(super) const FALSE_DOES_NOTHING: DoesNothing = DoesNothing {
attribute: "transparent(false)",
};
}
#[derive(Copy, Clone)]
pub(super) enum ErrorLocation {
OnEnum,
OnVariant,
InVariant,
OnField,
OnNamedStruct,
InNamedStruct,
OnTupleStruct,
OnTupleStructField,
}
impl fmt::Display for ErrorLocation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use ErrorLocation::*;
match self {
OnEnum => "on an enum".fmt(f),
OnVariant => "on an enum variant".fmt(f),
InVariant => "within an enum variant".fmt(f),
OnField => "on a field".fmt(f),
OnNamedStruct => "on a named struct".fmt(f),
InNamedStruct => "within a named struct".fmt(f),
OnTupleStruct => "on a tuple struct".fmt(f),
OnTupleStructField => "on a tuple struct field".fmt(f),
}
}
}
pub(super) trait ErrorForLocation {
fn for_location(&self, location: ErrorLocation, f: &mut fmt::Formatter) -> fmt::Result;
fn on(self, location: ErrorLocation) -> ErrorWithLocation<Self>
where
Self: Sized,
{
ErrorWithLocation(self, location)
}
}
#[derive(Copy, Clone)]
pub(super) struct ErrorWithLocation<T>(T, ErrorLocation);
impl<T> fmt::Display for ErrorWithLocation<T>
where
T: ErrorForLocation,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.for_location(self.1, f)
}
}
#[derive(Copy, Clone)]
pub(super) struct DoesNothing {
attribute: &'static str,
}
impl fmt::Display for DoesNothing {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "`{}` attribute has no effect", self.attribute)
}
}
#[derive(Copy, Clone)]
pub(super) struct OnlyValidOn {
attribute: &'static str,
valid_on: &'static str,
}
impl ErrorForLocation for OnlyValidOn {
fn for_location(&self, location: ErrorLocation, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"`{}` attribute is only valid on {}, not {}",
self.attribute, self.valid_on, location,
)
}
}
#[derive(Copy, Clone)]
pub(super) struct WrongField {
attribute: &'static str,
valid_field: &'static str,
}
impl fmt::Display for WrongField {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
r#"`{}` attribute is only valid on a field named "{}", not on other fields"#,
self.attribute, self.valid_field,
)
}
}
#[derive(Copy, Clone)]
pub(super) struct IncompatibleAttributes<'a> {
pub(super) attrs: &'a [&'static str],
pub(super) bonus: Option<&'static str>,
}
impl<'a> IncompatibleAttributes<'a> {
pub(super) const fn new(attrs: &'a [&'static str]) -> Self {
Self { attrs, bonus: None }
}
}
impl<'a> ErrorForLocation for IncompatibleAttributes<'a> {
fn for_location(&self, location: ErrorLocation, f: &mut fmt::Formatter) -> fmt::Result {
if let Some((attr0, attrs)) = self.attrs.split_first() {
write!(f, "`{}`", attr0)?;
let mut attrs = attrs.iter().peekable();
while let Some(attr) = attrs.next() {
if attrs.peek().is_some() {
write!(f, ", `{}`", attr)?;
} else {
write!(f, " and `{}`", attr)?;
}
}
}
write!(f, " may not be provided together {}", location)?;
if let Some(bonus) = self.bonus {
write!(f, ". {}.", bonus)?;
}
Ok(())
}
}
#[derive(Copy, Clone)]
pub(super) struct DuplicateAttribute {
attribute: &'static str,
}
impl ErrorForLocation for DuplicateAttribute {
fn for_location(&self, location: ErrorLocation, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Multiple `{}` attributes are not supported {}",
self.attribute, location,
)
}
}
#[derive(Copy, Clone)]
pub(super) struct DuplicateField {
field: &'static str,
}
impl ErrorForLocation for DuplicateField {
fn for_location(&self, location: ErrorLocation, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Multiple {} fields are not supported {}",
self.field, location,
)
}
}