#![doc = include_str!("../docs/traits.html")]
use std::ops::Range;
use manyhow::{span_range, SpanRanged};
use proc_macro2::{Ident, Span};
use quote::ToTokens;
use syn::ext::IdentExt;
use syn::parse::discouraged::Speculative;
#[cfg(doc)]
use syn::parse::ParseBuffer;
use syn::parse::{Parse, ParseStream};
use syn::token::Paren;
use syn::{parenthesized, Result, Token};
use crate::from_partial::FromPartial;
use crate::FromAttr;
pub trait AttributeNamed: AttributeBase {
const PREFERRED_OPEN_DELIMITER: &'static str = " = ";
const PREFERRED_CLOSE_DELIMITER: &'static str = "";
fn parse_named(
name: &'static str,
input: ParseStream,
) -> Result<Option<Named<SpannedValue<Self::Partial>>>>;
}
pub trait AttributePositional: AttributeBase {
fn parse_positional(input: ParseStream) -> Result<Option<SpannedValue<Self::Partial>>>;
}
pub trait AttributeValue: AttributeBase {
const EXPECTED: &'static str = "expected `=` or `(`";
const PREFERRED_OPEN_DELIMITER: &'static str = " = ";
const PREFERRED_CLOSE_DELIMITER: &'static str = "";
fn parse_value_meta(input: ParseStream) -> Result<SpannedValue<Self::Partial>> {
let content;
let paren = parenthesized!(content in input);
Self::parse_value(&content)
.map(SpannedValue::value)
.map(SpannedValue::with(paren.span.join()))
}
fn parse_value_eq(input: ParseStream) -> Result<SpannedValue<Self::Partial>> {
<Token![=]>::parse(input)?;
Self::parse_value(input)
}
fn parse_value(input: ParseStream) -> Result<SpannedValue<Self::Partial>>;
}
impl<T: AttributeValue> FromAttr for T {
fn parse_partial(input: ParseStream) -> Result<Self::Partial> {
Self::parse_value(input).map(SpannedValue::value)
}
}
impl<T: AttributeValue> AttributeNamed for T {
const PREFERRED_CLOSE_DELIMITER: &'static str = Self::PREFERRED_CLOSE_DELIMITER;
const PREFERRED_OPEN_DELIMITER: &'static str = Self::PREFERRED_OPEN_DELIMITER;
fn parse_named(
name: &'static str,
input: ParseStream,
) -> Result<Option<Named<SpannedValue<Self::Partial>>>> {
let Some(name) = parse_name(input, name) else {
return Ok(None);
};
let value = if input.peek(Token![=]) {
Self::parse_value_eq(input)?
} else if input.peek(Paren) {
Self::parse_value_meta(input)?
} else {
return Err(input.error(Self::EXPECTED));
};
Ok(Some(Named { name, value }))
}
}
pub trait PositionalValue {}
impl<T: AttributeValue + PositionalValue> AttributePositional for T {
fn parse_positional(input: ParseStream) -> Result<Option<SpannedValue<Self::Partial>>> {
Self::parse_value(input).map(Some)
}
}
pub trait AttributeMeta: AttributeBase {
fn parse_inner(input: ParseStream) -> Result<Self::Partial>;
}
impl<T: AttributeMeta> AttributeValue for T {
const EXPECTED: &'static str = "expected `(`";
const PREFERRED_CLOSE_DELIMITER: &'static str = ")";
const PREFERRED_OPEN_DELIMITER: &'static str = "(";
fn parse_value_eq(input: ParseStream) -> Result<SpannedValue<Self::Partial>> {
Err(input.error(Self::EXPECTED))
}
fn parse_value(input: ParseStream) -> Result<SpannedValue<Self::Partial>> {
Self::parse_inner(input).map(SpannedValue::call_site)
}
}
pub trait AttributePeekable {
fn peek(input: ParseStream) -> bool;
}
#[derive(Debug)]
pub struct Named<T> {
pub value: T,
pub name: Ident,
}
impl<T> Named<T> {
#[doc(hidden)]
pub fn error_span(&self) -> Span {
self.name.span()
}
}
impl<T> Named<SpannedValue<T>> {
pub fn value(self) -> T {
self.value.value
}
}
pub fn parse_name(input: ParseStream, name: &str) -> Option<Ident> {
let fork = &input.fork();
let ident: Ident = Ident::parse_any(fork).ok()?;
if ident == name {
input.advance_to(fork);
Some(ident)
} else {
None
}
}
pub trait AttributeBase: FromPartial<Self::Partial> {
type Partial;
}
#[derive(Debug)]
pub struct SpannedValue<T> {
pub value: T,
pub span: Range<Span>,
}
impl<T: Default> Default for SpannedValue<T> {
fn default() -> Self {
Self::call_site(Default::default())
}
}
impl<T> SpannedValue<T> {
pub fn value(self) -> T {
self.value
}
pub fn span(&self) -> Range<Span> {
self.span.clone()
}
pub fn map_value<I>(self, map: impl FnOnce(T) -> I) -> SpannedValue<I> {
SpannedValue {
span: self.span(),
value: map(self.value()),
}
}
pub(crate) fn with(span: impl SpanRanged) -> impl Fn(T) -> Self {
move |value| Self::new(value, span.span_range())
}
pub fn from_to_tokens(value: T) -> Self
where
T: ToTokens,
{
Self {
span: span_range!(value),
value,
}
}
pub fn new(value: T, span: impl SpanRanged) -> SpannedValue<T> {
Self {
value,
span: span.span_range(),
}
}
pub fn call_site(value: T) -> SpannedValue<T> {
Self::new(value, Span::call_site())
}
#[doc(hidden)]
pub fn error_span(&self) -> Span {
self.span()
.span_joined()
.unwrap_or_else(|| self.span().start)
}
}
impl<T> SpanRanged for SpannedValue<T> {
fn span_range(&self) -> Range<Span> {
self.span.clone()
}
}